Skip to content

Commit c66a42c

Browse files
authored
General content improvements from feedback (#51)
* General content improvements from feedback * Update model-viewer from 3.5.0 to 4.1.0 * Add support for playback of multiple animations at once. * Add tone-mapping to maintain rendering consistency in model-viewer 4.1.0 upgrade. Signed-off-by: Matias Codesal <mcodesal@nvidia.com>
1 parent 52b5e8e commit c66a42c

11 files changed

Lines changed: 501 additions & 16 deletions

File tree

docs/asset-modularity-instancing/authoring-point-instancing/point-instancing-intro.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ See the {usdcpp}`UsdGeomPointInstancer Details` documentation to learn more abou
1212

1313
Compared to [scenegraph instancing](../authoring-scenegraph-instancing/scenegraph-instancing-intro.md), PointInstancers can be less user-friendly and trickier to refine because most of scene description is hidden away in large arrays that are not as human-readable.
1414

15-
Point instancing is designed for massive numbers of simpler items where the overhead of an instance outweights the benefits of reuse. What does this mean? Think about leaves on a tree. You might have a 100,000 leaves on a tree. If you started down the path of using scenegraph instancing for this, you would need to define each leaf repetition and you would end up with 100,000 {term}`instanceable <Instanceable>` prims: "Leaf_000001", "Leaf_000002", "Leaf_000003", etc. Do you really need this in your scenegraph? Is this useful data for anyone?
15+
Point instancing is designed for massive numbers of simpler items where the overhead of an instance outweighs the benefits of reuse. What does this mean? Think about leaves on a tree. You might have a 100,000 leaves on a tree. If you started down the path of using scenegraph instancing for this, you would need to define each leaf repetition and you would end up with 100,000 {term}`instanceable <Instanceable>` prims: "Leaf_000001", "Leaf_000002", "Leaf_000003", etc. Do you really need this in your scenegraph? Is this useful data for anyone?
1616

1717
That's where point instancing is a clear winner.
1818

@@ -102,4 +102,23 @@ over "PackingPeanuts"
102102
}
103103
```
104104

105-
Keep this in mind as you perform downstream overrides and considerations you may need to make about things like USD {term}`layers <Layer>` format (i.e. USDA vs {term}`USDC <Crate File Format>`).
105+
Keep this in mind as you perform downstream overrides and considerations you may need to make about things like USD {term}`layers <Layer>` format (i.e. USDA vs {term}`USDC <Crate File Format>`).
106+
107+
## Computing Instance Transforms
108+
109+
When you specify `positions` on a PointInstancer, you're defining positions in the PointInstancer's local coordinate space. However, when USD computes the final world-space position of each instance, it combines multiple transforms together.
110+
111+
For each instance, USD applies the following transforms in order from most local to least local:
112+
113+
1. **Prototype root transform**: The local-to-parent transformation of the prototype root is applied most locally.
114+
2. **Instance-specific transforms**: The per-instance transformation is applied next, in this order:
115+
- `scales[i]` (if authored)
116+
- `orientations[i]` (if authored)
117+
- `positions[i]`
118+
3. **PointInstancer prim transform**: The transformation authored on the PointInstancer prim itself is applied least locally.
119+
120+
This means that you can move an entire PointInstancer around your scene by transforming the PointInstancer prim, and you can have prototypes with their own local transforms that will be incorporated into each instance's final position.
121+
122+
```{seealso}
123+
For the complete details on how instance transforms are computed, including handling of velocities and angular velocities, see the {usdcpp}`UsdGeomPointInstancer` documentation.
124+
```

docs/asset-modularity-instancing/instancing-faq.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@
1616
* Instances identifiable via index
1717
* Invasive deinstancing
1818
* May be combined with scenegraph instancing
19-
* Good for massive numbers of simpler items where the overhead of an instance outweights the benefits of reuse. (e.g. leaves on trees)
19+
* Good for massive numbers of simpler items where the overhead of an instance outweighs the benefits of reuse. (e.g. leaves on trees)

docs/asset-modularity-instancing/refining-point-instances.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,28 @@ box = stage.DefinePrim("/World/CubeBox_A04_26cm_01")
101101
stage_path = Path(stage.GetRootLayer().identifier)
102102
box_asset_path = stage_path.parent.parent / "src_assets" / "Assets" / "Components" / "CubeBox_A04_26cm" / "CubeBox_A04_26cm.usd"
103103
box.GetReferences().AddReference(str(box_asset_path))
104+
# Compute the transform for the instances.
105+
# This is more robust than GetPositionsAttr as it accounts for the prototype's transform and velocities too.
106+
xforms = pi.ComputeInstanceTransformsAtTime(
107+
Usd.TimeCode.Default(),
108+
Usd.TimeCode.Default(),
109+
# We can ignore the mask to query the previously deactivated instance
110+
applyMask=UsdGeom.PointInstancer.IgnoreMask)
111+
world_transform = xforms[1228]
112+
# Extract translation, rotation, and scale from the matrix
113+
translation = world_transform.ExtractTranslation()
114+
rotation = world_transform.ExtractRotation()
115+
scale = Gf.Vec3d(*(v.GetLength() for v in world_transform.ExtractRotationMatrix()))
116+
117+
# Apply the computed transform to the promoted box using Xformable API
104118
box_xform = UsdGeom.Xformable(box)
105-
box_xform.GetTranslateOp().Set(pi.GetPositionsAttr().Get()[1228])
106-
box_xform.AddOrientOp(precision=UsdGeom.XformOp.PrecisionHalf).Set(pi.GetOrientationsAttr().Get()[1228])
119+
box_xform.GetTranslateOp().Set(translation)
120+
box_xform.AddOrientOp().Set(Gf.Quatf(rotation.GetQuat()))
121+
box_xform.AddScaleOp().Set(scale)
107122
```
108123

124+
This approach uses `ComputeInstanceTransformsAtTime()` to get the complete transform matrix for instance `1228`, which automatically combines the prototype root transform and the instance-specific transform (positions, velocities, orientations, angularVelocities, scales). This is still slightly simplified because we did not account for the PointInstancer prim's transformations. In this case, it's ok because the PointInstancer is at the origin.
125+
109126
The box looks just like it did before in the Viewport, but now we have a new prim hierarchy in the scenegraph where we can author new {term}`opinions <Opinions>` to manipulate this asset.
110127

111128
![](../images/asset-modularity-instancing/promotion.png)

docs/asset-modularity-instancing/refining-scenegraph-instances/scenegraph-deinstance-refinement.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ The simplest way to edit or override an instance is to disable instancing for th
88
Deinstance refinement
99
```
1010

11-
At any point downstream, we can enable or disable instancing on a {term}`prim <Prim>`. If we have a use case where we just need to open one box, what we'll do is we'll set `instanceable = false` and now we can apply the overrides to open thet box.
11+
At any point downstream, we can enable or disable instancing on a {term}`prim <Prim>`. If we have a use case where we just need to open one box, what we'll do is we'll set `instanceable = false` and now we can apply the overrides to open that box.
1212

1313
If you just have one thing that needs to be promoted to be treated uniquely from the rest of the copies, it's totally reasonable to deinstance it in a stronger {term}`layer <Layer>`.
1414

docs/beyond-basics/custom-properties.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ Custom properties are the easiest and most flexible way to adapt OpenUSD to spec
6565
We often recommend custom properties instead of metadata or `customData` metadata for prototyping because the former requires plugin-based schema development which is less portable and the later is more costly for composition because it is a composable dictionary data type.
6666
```
6767

68+
### Grouping Related Properties
69+
70+
Namespace prefixes provide a way to logically group related properties together on a single prim. This is especially useful when working with data from other sources that have compound or structured types (like structs, records, or grouped fields). Since USD doesn't have a native struct type, namespace-prefixed attributes serve as the standard convention for representing this kind of grouped data.
71+
72+
You can even use nested namespaces to create a hierarchical organization. For example, sensor readings from an IoT device might be organized as `acme:sensor:temperature`, `acme:sensor:humidity`, and `acme:sensor:pressure`. Here, `acme:` identifies your organization and `sensor:` groups the related properties—the combined prefix makes it clear both where the data originated and that these attributes belong together conceptually. This approach is particularly valuable in data exchange workflows where you need to map complex data models from other formats into OpenUSD.
73+
6874
## Working With Python
6975

7076
![Custom Attribute Python](../images/foundations/CustomAttribute_Python.webm)
@@ -194,6 +200,59 @@ stage.Save()
194200
DisplayUSD(file_path, show_usd_code=True)
195201
```
196202

203+
### Example 3: Grouping Related Properties with Namespaces
204+
205+
When working with grouped or compound data from other sources, namespace-prefixed attributes provide a clean way to organize related properties together. This example demonstrates how to store sensor readings using namespace prefixes.
206+
207+
Notice the double namespacing pattern `acme:sensor:temperature`. The first namespace (`acme:`) identifies the organization that created these custom properties, while the second namespace (`sensor:`) groups related properties together. This hierarchical approach allows you to both claim ownership of your custom properties and logically organize them into functional groups. It's a common pattern when mapping compound data types from other formats into USD.
208+
209+
```{code-cell}
210+
:emphasize-lines: 9-20
211+
212+
from pxr import Usd, UsdGeom, Sdf
213+
214+
file_path = "_assets/sensor_data.usda"
215+
stage: Usd.Stage = Usd.Stage.CreateNew(file_path)
216+
217+
# Create a prim to represent a sensor device
218+
sensor_prim = stage.DefinePrim("/EnvironmentSensor", "Xform")
219+
220+
# Group related sensor readings using namespaces: "acme:sensor:"
221+
# "acme" identifies the organization, "sensor" groups the related properties
222+
temperature = sensor_prim.CreateAttribute("acme:sensor:temperature", Sdf.ValueTypeNames.Float, custom=True)
223+
humidity = sensor_prim.CreateAttribute("acme:sensor:humidity", Sdf.ValueTypeNames.Float, custom=True)
224+
pressure = sensor_prim.CreateAttribute("acme:sensor:pressure", Sdf.ValueTypeNames.Float, custom=True)
225+
timestamp = sensor_prim.CreateAttribute("acme:sensor:timestamp", Sdf.ValueTypeNames.String, custom=True)
226+
227+
# Document the custom properties to describe their purpose and units
228+
temperature.SetDocumentation("Temperature reading in degrees Celsius")
229+
humidity.SetDocumentation("Relative humidity as a percentage (0-100)")
230+
pressure.SetDocumentation("Atmospheric pressure in kilopascals (kPa)")
231+
timestamp.SetDocumentation("ISO 8601 formatted timestamp of the sensor reading")
232+
233+
# Set sensor readings
234+
temperature.Set(22.5)
235+
humidity.Set(45.0)
236+
pressure.Set(101.3)
237+
timestamp.Set("2025-01-09T14:30:00Z")
238+
239+
# Print grouped sensor data
240+
print("Sensor Readings:")
241+
print(f" Temperature: {temperature.Get()}°C")
242+
print(f" Humidity: {humidity.Get()}%")
243+
print(f" Pressure: {pressure.Get()} kPa")
244+
print(f" Timestamp: {timestamp.Get()}")
245+
246+
stage.Save()
247+
```
248+
249+
```{code-cell}
250+
:tags: [remove-input]
251+
DisplayCode(file_path)
252+
```
253+
254+
Notice how all the sensor-related attributes share the `acme:sensor:` prefix. The nested namespace structure (`organization:group:property`) makes it immediately clear both where these properties came from and that they belong together as a logical group, even though they're separate attributes. This pattern is especially useful when mapping structured data types (like structs or records) from other data sources into USD.
255+
197256
## Key Takeaways
198257

199258
Custom properties in OpenUSD provide a versatile way to extend the functionality of scene descriptions, making them adaptable to various specialized needs. By understanding how to create, set, and retrieve custom properties, we can enhance our OpenUSD workflows and better manage complex data in our projects, significantly improve the precision and efficiency of digital models, and build USD pipelines that are tailored to specific use

docs/beyond-basics/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ By the end of this module, you'll understand how to:
1313
- **Utilize {term}`model <Model>` {term}`kinds <Kind>`** - structure assets using {term}`component <Component>`, {term}`assembly <Assembly>`, and {term}`group <Group>` hierarchies
1414
- **{term}`Traverse stages <Stage Traversal>`** - implement high-performance iteration through complex scene graphs
1515
- **Understand {term}`Hydra <Hydra>` rendering** - work with USD's flexible rendering architecture and multiple backends
16+
- **Handle units in USD** - work with `metersPerUnit`, `upAxis`, `timeCodesPerSecond`, and understand automatic vs. manual unit reconciliation during composition
1617

1718
## Why These Skills Matter
1819

@@ -39,4 +40,5 @@ active-inactive-prims
3940
model-kinds
4041
stage-traversal
4142
hydra
43+
units
4244
:::

0 commit comments

Comments
 (0)