Problems accessing material attributes through replicator API

Hi

I am currently working on extending/adapting the material creation capabilities of the replicator.create API for my use case and ran into an error I cannot quite track down.

I reused the code of replicator.create.material_omnipbr and added the required inputs to the shader to be able to randomize different material properties.

This all works fine, except for the attributes that require 2-dimensional input, like translate or scale.
When I try to randomize those I get a signature Error from the C++ binding of the Pxr.USD API in the replicator pipeline.

[/Replicator/SDGPipeline] Assertion raised in compute - Python argument types in
    Vec2f.__init__(Vec2f, numpy.float32, numpy.float32)
did not match C++ signature:
    __init__(_object*, float, float)
    __init__(_object*, float)

However to me this actually looks fine and I also do not know how to further track down what is causing this error, so any help would be appreciated.

Below is the Code I am using:

  • Material Creation
@ReplicatorWrapper
def custom_material_create(
    diffuse: Tuple[float] = None,
    diffuse_texture: str = None,
    roughness: float = None,
    metallic: float = None,
    semantics: List[Tuple[str, str]] = None,
    count: int = 1,
) -> ReplicatorItem:

    attribute_types = {
        "diffuse_color_constant": "color3f",
        "diffuse_texture": "token",
        "reflection_roughness_constant": "float",
        "metallic_constant": "float",
        "texture_rotation": "float",
        "texture_translate": "float2",
        "texture_scale": "float2"
    }

    stage = omni.usd.get_context().get_stage()
    if not stage.GetPrimAtPath(REPLICATOR_SCOPE):
        stage.DefinePrim(REPLICATOR_SCOPE, "Scope")

    mdl = "OmniPBR.mdl"
    mtl_name, _ = os.path.splitext(mdl)

    if not stage.GetPrimAtPath(LOOKS_PATH):
        stage.DefinePrim(LOOKS_PATH, "Scope")

    prim_paths = []
    for _ in range(count):
        prim_path = omni.usd.get_stage_next_free_path(stage, f"{LOOKS_PATH}/{mdl.split('.')[0]}", False)
        utils._create_material(mtl_url=mdl, mtl_name=mtl_name, mtl_path=prim_path)
        shader = UsdShade.Shader(omni.usd.get_shader_from_material(stage.GetPrimAtPath(prim_path), True))

        shader.CreateInput("diffuse_color_constant", Sdf.ValueTypeNames.Color3f)
        shader.CreateInput("diffuse_texture", Sdf.ValueTypeNames.Asset)
        shader.CreateInput("reflection_roughness_constant", Sdf.ValueTypeNames.Float)
        shader.CreateInput("metallic_constant", Sdf.ValueTypeNames.Float)
        shader.CreateInput("texture_rotate", Sdf.ValueTypeNames.Float)
        shader.CreateInput("texture_translate", Sdf.ValueTypeNames.Float2)
        shader.CreateInput("texture_scale", Sdf.ValueTypeNames.Float2)

        prim_paths.append(prim_path)

    material_group = group(prim_paths)
    _set_attributes(
        material_group,
        attribute_types=attribute_types,
        semantics=semantics,
        diffuse_color_constant=diffuse,
        diffuse_texture=diffuse_texture,
        reflection_roughness_constant=roughness,
        metallic_constant=metallic
    )
    return material_group.node
  • Property randomization
with rep.new_layer("floor"):
        mat = custom_material_create(diffuse=rep.distribution.uniform((0,0,0), (1,1,1)))
        plane = rep.create.plane(scale=size, position=position, visible=True, material=mat)
        
        def randomize_floormat():
            with mat:
                rep.modify.attribute("diffuse_texture", rep.distribution.choice(floor_textures))
                rep.modify.attribute("texture_rotate", rep.distribution.uniform(texture_rotate["min"], texture_rotate["max"]))
                rep.modify.attribute("reflection_roughness_constant", rep.distribution.normal(roughness["mean"], roughness["std"]))
                rep.modify.attribute("texture_scale", rep.distribution.normal(texture_scale["mean"], texture_scale["std"]))
                rep.modify.attribute("texture_translate", rep.distribution.uniform(texture_translate["min"], texture_translate["max"]))
            return mat.node

        rep.randomizer.register(randomize_floormat)
    
        with rep.trigger.on_frame(interval=interval, rt_subframes=10):
            rep.randomizer.randomize_floormat()

So if anyone is interested, I found the problematic piece of code and a workaround that solves the issue for now.

As hinted above the cause of the problem is, that the Gf Vector constructor only accepts python floats, not numpy floats, that are returned from the OgnSampling nodes during runtime.

In the OgnWritePrimAttribute implementation of the compute() function I found a line that implements the necessary conversion for 3d vectors but for whatever reason does not for 2d vectors.

The lines are 240-244 in OgnWritePrimAttribute.py:

    typed_container = get_typed_container(attribute_type)

    # Make sure samples are floats if needed
    if typed_container == Gf.Vec3f or typed_container == Gf.Vec3d:
        samples = samples.astype(float)

By simply extending the if statement to also include Gf.Vec2f and Gf.Vec2d, randomization of 2d Vector attributes works as expected.
So after the change the line reads like this:

    typed_container = get_typed_container(attribute_type)

    # Make sure samples are floats if needed
    if  typed_container == Gf.Vec3f or \
        typed_container == Gf.Vec3d or \
        typed_container == Gf.Vec2f or \
        typed_container == Gf.Vec2d:
            samples = samples.astype(float)

Hi @ManicMunchkin thanks for bringing this up. Glad you found a workaround. I ran this by the Replicator devs and we can add support for numpy floats in a future release.

1 Like

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