Removing prims between frames

Hi,
Is it possible to remove prims and generate new ones between frames?
For example, generating a random number of spheres and cubes, scattering them on a plane for one frame.
Then removing those and generating a random number of spheres and cubes, scattering them on the same plane the next frame, but with different numbers of spheres and cubes.
I am unable to find any methods to remove previously generated prims in the API.
Help on the matter would be appreciated.

Thanks

import omni.replicator.core as rep
import numpy as np

NUM_SPAWN = 4

with rep.new_layer():
    l_plane = rep.create.plane(scale=10, position=(-500, 0, 0), visible=False)
    r_plane = rep.create.plane(scale=10, position=(500, 0, 0), visible=False)


    def generate_shapes():
        rand_spawn = np.random.randint(2, size=NUM_SPAWN)
        print(rand_spawn)
        for i in range(len(rand_spawn)):
            if rand_spawn[i] == 1:
                if i < (NUM_SPAWN / 2):
                    rep.create.cube(semantics=[('class', 'cube'), ('side', 'left')])
                else:
                    rep.create.cube(semantics=[('class', 'cube'), ('side', 'right')])
            else:
                if i < (NUM_SPAWN / 2):
                    rep.create.sphere(semantics=[('class', 'sphere'), ('side', 'left')])
                else:
                    rep.create.sphere(semantics=[('class', 'sphere'), ('side', 'right')])


    def modify_shape_pos_left():
        left_shapes = rep.get.prims(semantics=[('side', 'left')])

        with left_shapes:
            rep.randomizer.scatter_2d(surface_prims=l_plane, check_for_collisions=True)
            return left_shapes.node


    def modify_shape_pos_right():
        right_shapes = rep.get.prims(semantics=[('side', 'right')])

        with right_shapes:
            rep.randomizer.scatter_2d(surface_prims=r_plane, check_for_collisions=True)
            return right_shapes.node


    rep.randomizer.register(generate_shapes)
    rep.randomizer.register(modify_shape_pos_right)
    rep.randomizer.register(modify_shape_pos_left)

    camera = rep.create.camera(position=(1349.07818, 1648.22257, 2048.22328), look_at=(0, 0, 0), focal_length=38)
    render_product = rep.create.render_product(camera, (1280, 720))

    writer = rep.WriterRegistry.get("BasicWriter")
    writer.initialize(output_dir="", rgb=True, bounding_box_2d_tight=True)
    writer.attach([render_product])

    with rep.trigger.on_frame(num_frames=50, rt_subframes=60):
        rep.randomizer.generate_shapes()
        rep.randomizer.modify_shape_pos_right()
        rep.randomizer.modify_shape_pos_left()

Here’s the code i used. The cubes & spheres are generated on first spawn, and do not change between frames. I would like to have it so every frame has a random combination of cube and spheres.

If I understand what you are trying to randomize correctly, something like this should work:

import omni.replicator.core as rep

NUM_SPAWN = 4

with rep.new_layer():
    l_plane = rep.create.plane(scale=10, position=(-500, 0, 0), visible=False)
    r_plane = rep.create.plane(scale=10, position=(500, 0, 0), visible=False)

    spheres_l = rep.create.sphere(count=NUM_SPAWN, semantics=[('class', 'sphere'), ('side', 'left')])
    cubes_l = rep.create.cube(count=NUM_SPAWN, semantics=[('class', 'cube'), ('side', 'left')])
    spheres_r = rep.create.sphere(count=NUM_SPAWN, semantics=[('class', 'sphere'), ('side', 'right')])
    cubes_r = rep.create.cube(count=NUM_SPAWN, semantics=[('class', 'cube'), ('side', 'right')])
        
    def randomize_shapes():
        all_group = rep.create.group([spheres_l, cubes_l, spheres_r, cubes_r])
        with all_group:
            rep.modify.visibility(rep.distribution.choice([True, False]))

        left_group = rep.create.group([spheres_l, cubes_l])
        right_group = rep.create.group([spheres_r, cubes_r])

        with left_group:
            rep.randomizer.scatter_2d(surface_prims=l_plane)

        with right_group:
            rep.randomizer.scatter_2d(surface_prims=r_plane)

    rep.randomizer.register(randomize_shapes)

    camera = rep.create.camera(position=(1349.07818, 1648.22257, 2048.22328), look_at=(0, 0, 0), focal_length=38)
    render_product = rep.create.render_product(camera, (1280, 720))

    writer = rep.WriterRegistry.get("BasicWriter")
    writer.initialize(output_dir="forum_test", rgb=True, bounding_box_2d_tight=True)
    writer.attach([render_product])

    with rep.trigger.on_frame(num_frames=50, rt_subframes=60):
        rep.randomizer.randomize_shapes()

Basically, you’ll want to create all the shapes you want to randomize first in addition to the planes where you want them to be scattered. Then you can group them all and randomly toggle their visibility, and then scatter the “left” and “right” shapes on the appropriate plane.

Hopefully this helps!

Would it be possible to implement a better way to do this? For my purposes I am planning to have >10 different types of USD imported in place of the sphere/cube as well as >5 planes for them to be scattered across, and using this method would result in a large mass of code and unused prims.

Take a look at this example

Randomizing appearance, placement and orientation of an existing 3D assets with a built-in writer — Omniverse Extensions documentation (nvidia.com)

This instanciates a number of plant props in a scene. You can modify it to use scatter_2d to place them on planes

Is there a way to ensure annotation labels are saved with that method? I have tried saving usd files with annotations before but apparently the annotations aren’t saved? Is there a way to modify the usd files such that they are annotated?

Which annotations do you mean?

Semantic annotations should be able to be saved on the objects- though depending on how you save it could be in an override layer of USD instead of the original USD file.

For standard Replicator Annotators - they would be output to disk via a writer