Harmonizer

A harmonizer defines the relationship among randomized mutable attributes.

If the key-value pair in the description file has a key of harmonizer_type, it defines a harmonizer. A harmonizer constrains how a mutable attribute randomizes. For example:

rotate_H:
  harmonizer_type: mutable_attribute
  mutable_attribute:
    distribution_type: range
    start: -180
    end: 180

Mutable attribute harmonizer (Deprecated)

If harmonizer_type is a mutable_attribute, it is a mutable attribute harmonizer. When the harmonizers harmonize, a value is resolved for it just as in any other mutable attributes.

To randomize a value that is shared across different mutable attributes, but is different across different frames. Consider a scene where there are two OROs standing side by side:

oro:
  count: 2
  type: geometry
  subtype: mesh
  usd_path: [PATH_TO_ORO]
  transform_operators:
  - translate:
    - ($[../index] % 2 - 0.5) * 600
    - 0
    - 0
../../_images/harmonizer-1.png

Use rotateY to rotate them by 30 degrees each:

transform_operators:
- translate:
  - ($[../index] % 2 - 0.5) * 600
  - 0
  - 0
- rotateY: 30
../../_images/harmonizer-2.png

But if you specify rotateY as a mutable attribute, they rotate differently within the same frame:

transform_operators:
- translate:
  - ($[../index] % 2 - 0.5) * 600
  - 0
  - 0
- rotateY:
    distribution_type: range
    start: -180
    end: 180
../../_images/harmonizer-3.png

To have them rotate the same angle in the same frame, but different angles across different frames use the harmonizer. To define a harmonizer:

rotate_H:
  harmonizer_type: mutable_attribute
  mutable_attribute:
    distribution_type: range
    start: -180
    end: 180

This evaluates only once per frame in the harmonize stage, then the ORO mutables retrieve this value per frame from the harmonizer:

- rotateY:
  distribution_type: harmonized
  harmonizer_name: rotate_H

If they rotate the same amount:

../../_images/harmonizer-4.png ../../_images/harmonizer-5.png

This can be used in all other transform operators and all other distribution types.

Note

Now that standalone mutable attributes are supported in the description symbol resolution process, mutable attribute harmonizers are no longer needed, allowing for more streamlined code. For example:

rotate_H:
  distribution_type: range
  start: -180
  end: 180
oro:
  count: 2
  type: geometry
  subtype: mesh
  usd_path: [PATH_TO_ORO]
  transform_operators:
  - translate:
    - ($[../index] % 2 - 0.5) * 600
    - 0
    - 0
  - rotateY: $[/rotate_H]

Permutation harmonizer

If harmonizer_type is permutate, it is a permutation harmonizer. When you free randomize a harmonized mutable attribute, you can specify a pitch as the input to the permutation harmonizer. Then the permutation harmonizer shuffles these inputs and sends back the value to the harmonized attribute, which in turn can be used in a transform operator in the harmonized randomize stage.

For example, to define three OROs, facing in three directions:

oro:
  count: 3
  type: geometry
  subtype: mesh
  usd_path: [PATH_TO_ORO]
  transform_operators:
  - translate:
    - ($[../index] % $[../count] - 1) * 600
    - 0
    - 0
  - rotateY: ($[../index] - 1) * 60

These three OROs have X-axis position -600, 0, 600; and they are rotated around Y-axis by -60, 0, 60 degrees.

../../_images/harmonizer-6.png

To shuffle the positions of these OROs, so that the ORO that rotates -60 degrees can appear to the right:

Define a mutable attribute permutated_index as harmonized. During the free randomize stage, it submits its index as its pitch to the harmonizer permutate_H, which is a permutation harmonizer.

oro:
  ...
  permutated_index:
    distribution_type: harmonized
    harmonizer_name: permutate_H
    pitch: $[index]
permutate_H:
  harmonizer_type: permutate

During the harmonize stage, permutate_H shuffles the received pitches from all relevant harmonized mutable attributes and resonates them back to each of them.

During the harmonized randomize stage, permutated_index gets the shuffled value back. So you can use it in transform operators just like using an index.

oro:
  ...
  transform_operators:
  - translate:
    - ($[permutated_index] % $[../count] - 1) * 600
    - 0
    - 0
  - rotateY: ($[../index] - 1) * 60
../../_images/harmonizer-7.png ../../_images/harmonizer-8.png

This feature can be used with any values within scope.

Bin pack harmonizer

If harmonizer_type is bin_pack, it is a bin pack harmonizer that packs objects into a cuboid space according to their axis-aligned bounding boxes. You can define a cuboid with custom dimensions like this:

bin_pack_H:
  harmonizer_type: bin_pack
  bin_size:
  - 480
  - 260
  - 700

You can define many OROs, and pack them into this cuboid:

oro:
  count: 200
  physics: rigidbody
  type: geometry
  subtype: mesh
  tracked: true
  transform_operators:
  - translate:
    - 0
    - 300
    - 0
  - transform:
      distribution_type: harmonized
      harmonizer_name: bin_pack_H
      pitch: local_aabb
  - scale:
    - 30
    - 30
    - 30
  usd_path: PATH_TO_ORO

For example, with many OROs densely packed together:

../../_images/harmonizer-bin-pack.png

In this example, 200 OROs are spawned during initialization.

At free randomize stage, the usd_path of mutable oro is determined, which can be harmonized, randomized regularly, or just a constant value; at the same stage, the transform transform operator, which is a harmonized mutable attribute that retrives its value from bin_pack_H, submits the prims’ local axis-aligned bounding boxes to bin_pack_H by specifying pitch: local_aabb.

While at harmonize stage<simulation workflow>, bin_pack_H collects all those bounding boxes and determines the transform for each of these boxes; the boxes that don’t fit in are moved to invisible areas.

Finally, at harmonized randomize stage, each oro retrives their transforms and goes to their allocated position and orientation.