Hello,
I am using Replicator for randomizing the positions of items in a scene. I want to modify the positions such that the items move about their own local axis (i.e. they translate locally rather than globally). My current code looks like this:
import omni.replicator.core as rep
import random
seed = random.randint(545631, 1000000)
rep.set_global_seed(seed)
with rep.new_layer():
camera = rep.create.render_product(
"/Camera",
resolution=(1920, 1080)
)
def item_randomizer():
items = rep.get.prims(path_pattern='items', prim_types=['Xform'])
with items:
rep.modify.pose(
position_x = rep.distribution.uniform(-30, 30),
)
rep.randomizer.register(item_randomizer)
with rep.trigger.on_frame(num_frames=10):
rep.randomizer.item_randomizer()
basic_writer = rep.WriterRegistry.get("BasicWriter")
basic_writer.initialize(
output_dir="_Output",
rgb=True,
)
basic_writer.attach([camera])
rep.orchestrator.run()
The command rep.modify.pose(position_x = rep.distribution.uniform(-30, 30),) moves all the items randomly to a global position between -30 and 30. What I want to do is that the items translate about their own current position by certain millimeters. I have searched far and wide but have not been able to find a solution for this. I would appreciate some insight on this topic.
I apologize for the delay in getting back to you and we appreciate your feedback! This is a known limitation that we are in the process of addressing. In the mean time, you can achieve relative positioning by creating your own custom node. Here’s an example based on your script above. Please reach out if you have any questions!
from typing import List
import pxr
import omni.replicator.core as rep
import random
import omni.graph.core as og
import omni.graph.core.types as ot
import omni.usd
# Create a new type of node
node_type_name = "omni.replicator.core.jitter"
# Only create new node if it doesn't exist
try:
og.ObjectLookup.node_type(node_type_name)
except og.OmniGraphError:
@og.create_node_type(unique_name=node_type_name, add_execution_pins=True)
def jitter(prims: ot.target, relative_positions: ot.float3array, jitters: ot.float3array):
"""Modify prim position relative to an existing position"""
# Verify same number of prims to targets and jitters
assert len(prims) == len(relative_positions)
assert len(prims) == len(jitters)
# Assume prim already has `xform:translate` op
stage = omni.usd.get_context().get_stage()
for prim_path, relative_position, jitter in zip(prims, relative_positions, jitters):
prim = stage.GetPrimAtPath(str(prim_path))
if not prim.HasAttribute("xformOp:translate"):
raise ValueError(f"Prim {prim.GetPath()} missing translate op")
prim.GetAttribute("xformOp:translate").Set(tuple((relative_position + jitter).tolist()))
# Integrate with replicator
@rep.utils.ReplicatorWrapper
def jitter(relative_positions: List[pxr.Gf.Vec3f], jitters: rep.utils.ReplicatorItem) -> og.Node:
node = rep.utils.create_node(node_type_name, relative_positions=relative_positions)
# Setup the dynamic attribute connection
rep.utils._setup_random_attribute(node, input_name="jitters", attribute_value=jitters)
return node
rep.modify.register(jitter)
# Test
rep.settings.set_stage_meters_per_unit(1)
rep.settings.set_stage_up_axis("Z")
with rep.new_layer():
depth = 5
cubes = []
for i in range(depth):
cubes.append(rep.create.cube(position=(i * 110, 0, 0)))
def item_randomizer():
items = rep.get.prims(path_pattern='_Xform', prim_types=['Xform'])
# Get existing baseline positions
positions = []
for prim in items.get_output_prims()["prims"]:
if not prim.HasAttribute("xformOp:translate"):
raise ValueError(f"Prim {prim.GetPath()} missing translate op")
positions.append(prim.GetAttribute("xformOp:translate").Get())
with items:
print(positions)
rep.modify.jitter(
relative_positions=positions,
jitters=rep.distribution.uniform((-30, 0, 0), (30, 0, 0))
)
rep.randomizer.register(item_randomizer)
with rep.trigger.on_frame(num_frames=10):
rep.randomizer.item_randomizer()
rep.orchestrator.run()
Note, another option which can be much simpler is to make use of USD’s hierarchy. By arranging prims in a tree, we can effectively achieve the same result using the built-in Replicator nodes.