Materials out of range

I’m using the Partnet data set, where each part of the object has to be loaded in separately, and I’ve been trying to randomise the colours/materials per sub object.
For instance, I might want all the parts of a drawer to be the same colour. To do this, I’ve basically needed to create callbacks of varying name and so I’ve had to use monkey patching. (If there’s a better way, please do say. This is a recipie for many bugs!).

The core of the code I’ve got is basically

def randomiseColoursMaterialsByCategory(self, materials):
    num_colour_series = len(MAPPING_LABELS) + 1;
    colour_rand_series = [];
    for i in range(num_colour_series):
        appending = [];
        for j in range(self.num_iterations):
            appending.append((random.uniform(0,255), random.uniform(0,255), random.uniform(0,255)));
        colour_rand_series.append(appending);


    keys = list(MAPPING_LABELS.keys());
    keys.append(NOT_IN_MAPPING_LABEL);
    for i in range(len(keys)):

        func_name = "randomise_item_set_colours_{0}_{1}".format(str(self.uid), str(i));

        # self.internal_func_store.append(randomise_sub_obj_colour);
        print(keys[i]);
        print(type(keys[i]));
        evaluating = (
            "def {0}():" + 
            "\n\tfurniture_items = rep.get.prims(semantics=[( FURNITURE_PART, LABEL )]);" + 
            "\n\twith furniture_items:" + 
            "\n\t\trep.randomizer.materials(materials); "
            "\n\t\trep.randomizer.color(rep.distribution.sequence(colour_rand_series[i])); " + 
            "\n\treturn furniture_items.node;" + 
            # "\nprint(\"Func name:\", func_name);" + 
            "\nrep.randomizer.register({0});" + 
            "\nself.callbacks.append( lambda : rep.randomizer.{0}() );" + 
            "\nself.internal_func_store.append({0});").format(func_name);

        # print(evaluating);

        exec(
            evaluating, 
            {"rep":rep, "self":self, "materials":materials, 
            "FURNITURE_PART":FURNITURE_PART, "LABEL": MAPPING_LABELS[keys[i]] + "_" + str(self.uid),
            "colour_rand_series":colour_rand_series, "i":i});

When running this, all the arguments go in correctly, but I then get the error

2022-11-23 10:33:15 [554,635ms] [Error] [omni.graph.core.plugin] Assertion raised in compute - list index out of range
  File "/home/matthewmunks/.local/share/ov/pkg/deps/775e081cef5fe932262e6117bb022b51/extscache/omni.replicator.core-1.4.4+lx64.r.cp37/omni/replicator/core/ogn/python/_impl/nodes/OgnSampleOmniPBR.py", line 152, in compute
    mat_params.diffuse = tuple(diffuse_samples[idx])

This happens even if I randomise the materials in a separate callback function.
However it does not happen when not using the monkey-patch solution and I just randomise all the colours and materials.


EDIT:
A closed form example would be

import omni.replicator.core as rep

import random;

num_iterations = 5;

with rep.new_layer():
    materials = rep.create.material_omnipbr(diffuse=rep.distribution.uniform((0,0,0), (1,1,1)), count=30);


    camera = rep.create.camera(
        position=(50,50,50),
        look_at=(0,0,0),
        look_at_up_axis=(0,1,0));
    render_product = rep.create.render_product(camera, (640,480));

    def create_sphere_lights(num):
        lights = rep.create.light(light_type="Sphere", temperature=rep.distribution.normal(6500,500), intensity=rep.distribution.normal(5000,1000), position=rep.distribution.uniform((0,0,0),(50,50,50)), scale=rep.distribution.uniform(50,100), count=num);
        return lights.node;
    rep.randomizer.register(create_sphere_lights);

    # Overall setup. Lots of objects of multiple different clases being created.
    cones = rep.create.cone(count=100, position=rep.distribution.uniform((-100,-100,-100),(100,100,100)), scale=rep.distribution.uniform(1,5), semantics=[("class","cone")]);
    cubes = rep.create.cone(count=100, position=rep.distribution.uniform((-100,-100,-100),(100,100,100)),scale=rep.distribution.uniform(1,5),semantics=[("class","cube")]);

    CLASSES_OF_INTEREST = ["cone", "cube"];

    callbacks = [];

    num_colour_series = len(CLASSES_OF_INTEREST);
    colour_rand_series = [];
    for i in range(num_colour_series):
        appending = [];
        for j in range(num_iterations):
            appending.append((random.uniform(0,255), random.uniform(0,255), random.uniform(0,255)));
        colour_rand_series.append(appending);

    for i in range(len(CLASSES_OF_INTEREST)):

        func_name = "randomise_item_set_colours_{0}".format(str(i));

        evaluating = (
            "def {0}():" + 
            "\n\tfurniture_items = rep.get.prims(semantics=[( \"class\", LABEL )]);" + 
            "\n\twith furniture_items:" + 
            "\n\t\trep.randomizer.materials(materials); "
            "\n\t\trep.randomizer.color(rep.distribution.sequence(colour_rand_series[i])); " + 
            "\n\treturn furniture_items.node;" + 
            # "\nprint(\"Func name:\", func_name);" + 
            "\nrep.randomizer.register({0});" + 
            "\ncallbacks.append( lambda : rep.randomizer.{0}() );").format(func_name);


        exec(
            evaluating, 
            {"rep":rep, "materials":materials, 
            "LABEL": CLASSES_OF_INTEREST[i],
            "colour_rand_series":colour_rand_series, "i":i,
            "callbacks":callbacks});
    
    with rep.trigger.on_frame(num_frames=num_iterations):
        rep.randomizer.create_sphere_lights(1);
        for callback in callbacks:
            callback();

        # rep.randomizer.move_camera();

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

rep.orchestrator.run();

Hi @matthewmunks. Checking with the devs on this.

Hi @matthewmunks. I’ve created internal ticket OM-75781 for that dev team to look into this.

Hi @matthewmunks . Would something like this work for you?

import random;
import omni.replicator.core as rep

num_iterations = 5;

with rep.new_layer():
    camera = rep.create.camera(
        position=(50,50,50),
        look_at=(0,0,0),
        look_at_up_axis=(0,1,0));
    render_product = rep.create.render_product(camera, (640,480));

    def create_sphere_lights(num):
        lights = rep.create.light(light_type="Sphere", temperature=rep.distribution.normal(6500,500), intensity=rep.distribution.normal(5000,1000), position=rep.distribution.uniform((0,0,0),(50,50,50)), scale=rep.distribution.uniform(50,100), count=num);
        return lights.node;
    rep.randomizer.register(create_sphere_lights);

    # Overall setup. Lots of objects of multiple different clases being created.
    cones = rep.create.cone(count=100, position=rep.distribution.uniform((-1000,-100,-1000),(1000,100,1000)), scale=rep.distribution.uniform(1,5), semantics=[("class","cone")]);
    cubes = rep.create.cone(count=100, position=rep.distribution.uniform((-1000,-100,-1000),(1000,100,1000)),scale=rep.distribution.uniform(1,5),semantics=[("class","cube")]);

    CLASSES_OF_INTEREST = ["cone", "cube"];

    with rep.trigger.on_frame(num_frames=num_iterations):
        rep.randomizer.create_sphere_lights(1);
        with rep.get.prims(semantics=[( "class", label ) for label in CLASSES_OF_INTEREST]):
            rep.randomizer.color(rep.distribution.uniform((0,0,0), (1, 1, 1)))
        # rep.randomizer.move_camera();

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

Hi @pcallender

So the whole point of doing all of the above is so that, in the toy example say, the cones are all the same colour and the cubes are all the same colour. I don’t believe just using rep.distribution.uniform(…) will do this.

The other approach would probably be to use step async as per

import random;
import asyncio;
import omni.replicator.core as rep

num_iterations = 5;

async def main():
    camera = rep.create.camera(
        position=(50,50,2000),
        look_at=(0,0,0),
        look_at_up_axis=(0,1,0));
    render_product = rep.create.render_product(camera, (640,480));

    def create_sphere_lights(num):
        lights = rep.create.light(light_type="Sphere", temperature=rep.distribution.normal(6500,500), intensity=rep.distribution.normal(5000,1000), position=rep.distribution.uniform((0,0,0),(50,50,50)), scale=rep.distribution.uniform(50,100), count=num);
        return lights.node;
    rep.randomizer.register(create_sphere_lights);

    # Overall setup. Lots of objects of multiple different clases being created.
    cones = rep.create.cone(count=50, position=rep.distribution.uniform((-1000,-100,-1000),(1000,100,1000)), scale=rep.distribution.uniform(1,5), semantics=[("class","cone")]);
    cubes = rep.create.cube(count=50, position=rep.distribution.uniform((-1000,-100,-1000),(1000,100,1000)),scale=rep.distribution.uniform(1,5),semantics=[("class","cube")]);

    CLASSES_OF_INTEREST = ["cone", "cube"];

    with rep.trigger.on_frame():
        rep.randomizer.create_sphere_lights(1);

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

    for i in range(num_iterations):
        for class_ in CLASSES_OF_INTEREST:
            material = rep.create.material_omnipbr(diffuse=rep.distribution.uniform((0,0,0), (1,1,1)), count=1);
            colour = (random.random(), random.random(), random.random());
            print(class_, colour);
            with rep.get.prims(semantics=[( "class", class_)]):
                rep.randomizer.materials(material);
                rep.randomizer.color(colour, per_sub_mesh=True);

        print("Pre step async");
        await rep.orchestrator.step_async();
        print("Post step async");

    rep.orchestrator.stop()

asyncio.ensure_future(main());

but for some reason I can’t get this to work either.

@matthewmunks Thanks for the extra info. I’ve run this by the eng team and will get back to you when I have an update.

Hi @matthewmunks . Please try the below example. The eng. team came back with this example. Let me know if this solves the issue for you.

import omni.replicator.core as rep

num_iterations = 5;

with rep.new_layer():
    camera = rep.create.camera(
        position=(50,50,50),
        look_at=(0,0,0),
        look_at_up_axis=(0,1,0));
    render_product = rep.create.render_product(camera, (640,480));

    def create_sphere_lights(num):
        lights = rep.create.light(light_type="Sphere", temperature=rep.distribution.normal(6500,500), intensity=rep.distribution.normal(5000,1000), position=rep.distribution.uniform((0,0,0),(50,50,50)), scale=rep.distribution.uniform(50,100), count=num);
        return lights.node;
    rep.randomizer.register(create_sphere_lights);

    # Overall setup. Lots of objects of multiple different clases being created.
    cones = rep.create.cone(count=100, position=rep.distribution.uniform((-1000,-100,-1000),(1000,100,1000)), scale=rep.distribution.uniform(1,5), semantics=[("class","cone")]);
    cubes = rep.create.cone(count=100, position=rep.distribution.uniform((-1000,-100,-1000),(1000,100,1000)),scale=rep.distribution.uniform(1,5),semantics=[("class","cube")]);

    CLASSES_OF_INTEREST = ["cone", "cube"];

    with rep.trigger.on_frame(num_frames=num_iterations):
        rep.randomizer.create_sphere_lights(1);
        for label in CLASSES_OF_INTEREST:
            material = rep.create.material_omnipbr(diffuse=rep.distribution.uniform((0,0,0), (1, 1, 1)))
            with rep.get.prims(semantics=( "class", label )):
                rep.randomizer.materials(material)

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

Yes, that did work. Thank you and sorry for my slow response.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.