Setting Shader Albedo Map in Code - Instance creation timing?

We reviewed this in Dev Office hours on 08/12/22. I’m having a slight problem with the Shader Image thing. We reviewed this code to set the shader diffuse_texture to use an image.

Start from a Fresh Stage… The script fails the first time you run it in the Script Editor, but succeeds the second time. It seems maybe a timing issue that the shader prim doesnt really exist yet? maybe a msg pump hasn’t had time to finish? I am creating the prim, the shader, changing the image all in 1 script. (I have a test png at c:\tmp\test.png)

It seems to work the second time because it uses the original shader, not the new testplane_01 it creates the second run. So that seems to be the key, the msg pump has run and the testplane does exist on the second run.

How can I fix this?

This fails in script editor:

from pxr import Usd, UsdGeom, Sdf
import omni.usd
import omni.kit.commands

prim_path = "/World/Plane"
prim_name ="testplane"

#Get the stage
stage = omni.usd.get_context().get_stage()

#Create a Plane Prim
omni.kit.commands.execute('CreatePrimWithDefaultXform',prim_type='Plane', prim_path=path )

#Select the Plane
omni.kit.commands.execute('SelectPrims',old_selected_paths=[''],new_selected_paths=[prim_path],expand_in_stage=True)

#Create a Shader
omni.kit.commands.execute('CreateAndBindMdlMaterialFromLibrary',mdl_name='OmniPBR.mdl',mtl_name='OmniPBR',prim_name=prim_name,mtl_created_list=None,bind_selected_prims=True)

#Get the Shader
shader_prim = stage.GetPrimAtPath("/World/Looks/testplane/Shader")

print(shader_prim.GetAttribute("inputs:diffuse_texture"))
print(shader_prim.GetAttribute("inputs:diffuse_texture").Set("c:\\tmp\\test.png"))

print(shader_prim.GetAttributes())
print(shader_prim)

First run:

Second run:

Hi @gavin.stevens. You are correct. There are some asynchronous parts in there that are causing the error. You can wait for updates to propagate to make this work. I’ll give the dev team a heads up to see if they can simplify this.

from pxr import Usd, UsdGeom, Sdf
import omni.usd
import omni.kit.commands
import asyncio
import omni.kit.app

async def create_plane_and_mtl_async():
    prim_path = "/World/Plane"
    prim_name ="testplane"
    
    #Get the stage
    stage = omni.usd.get_context().get_stage()
    
    #Create a Plane Prim
    omni.kit.commands.execute('CreatePrimWithDefaultXform', prim_type='Plane', prim_path=prim_path )
    
    #Select the Plane
    omni.kit.commands.execute('SelectPrims',old_selected_paths=[''],new_selected_paths=[prim_path],expand_in_stage=True)
    
    #Create a Shader
    omni.kit.commands.execute('CreateAndBindMdlMaterialFromLibrary',mdl_name='OmniPBR.mdl',mtl_name='OmniPBR',prim_name=prim_name,mtl_created_list=None,bind_selected_prims=True)
    await omni.kit.app.get_app().next_update_async()
    
    selection = omni.usd.get_context().get_selection()
    selection.set_selected_prim_paths(["/World/Looks/testplane/Shader"], False)
    await omni.kit.app.get_app().next_update_async()
    #Get the Shader
    shader_prim = stage.GetPrimAtPath("/World/Looks/testplane/Shader")
    omni.kit.commands.execute('ChangeProperty',
        prop_path=Sdf.Path('/World/Looks/testplane/Shader.inputs:diffuse_texture'),
        value="./test.png",
        prev="./test.png"
)
asyncio.ensure_future(create_plane_and_mtl_async())
1 Like

Thanks, I’ll give that a shot

Even in Async context I seem to be struggling with this…

This is the code:

asyncio.ensure_future(self.draw_stage_async(str(prim_path))

async def draw_stage_async(self, prim_path: str, prim_name: str):

       #Select the Plane
        omni.kit.commands.execute('SelectPrims',
            old_selected_paths=[''],
            new_selected_paths=[prim_path],
            expand_in_stage=True)

        print("Creating Shader: " + prim_name)

        #Create a Shader
        omni.kit.commands.execute('CreateAndBindMdlMaterialFromLibrary',
            mdl_name='OmniPBR.mdl',
            mtl_name='OmniPBR',
            prim_name=prim_name,
            mtl_created_list=None,
            bind_selected_prims=True)

        #Wait for a few updates
        for x in range(5):
            await omni.kit.app.get_app().next_update_async()
        
        #Get the Shader and set the image property
        shader_path = Sdf.Path("/Looks")
        shader_path = shader_path.AppendPath(prim_name)
        shader_path = shader_path.AppendPath("Shader")

        print("Getting new shader: " + str(shader_path))

        stage = omni.usd.get_context().get_stage()
        shader = stage.GetPrimAtPath(str(shader_path))
        
        print("Shader Attributes:-----")
        print(shader.GetAttributes())

#Fails here
        texture = shader.GetAttribute("inputs:diffuse_texture")

        if (texture is not None):
            texture.Set(str(output_file))
        
        print(shader.GetAttribute("inputs:diffuse_texture"))

And the logs:

2022-08-16 00:49:21  [Info] [omni.kit.app.impl] [py stdout]: Getting new shader: /Looks/backups/Shader
2022-08-16 00:49:21  [Info] [omni.kit.app.impl] 
2022-08-16 00:49:21  [Info] [omni.kit.app.impl] [py stdout]: Shader Attributes:-----
2022-08-16 00:49:21  [Info] [omni.kit.app.impl] 
2022-08-16 00:49:21  [Info] [omni.kit.app.impl] [py stdout]: [Usd.Prim(</Looks/backups/Shader>).GetAttribute('info:id'), Usd.Prim(</Looks/backups/Shader>).GetAttribute('info:implementationSource'), Usd.Prim(</Looks/backups/Shader>).GetAttribute('info:mdl:sourceAsset'), Usd.Prim(</Looks/backups/Shader>).GetAttribute('info:mdl:sourceAsset:subIdentifier'), Usd.Prim(</Looks/backups/Shader>).GetAttribute('inputs:excludeFromWhiteMode'), Usd.Prim(</Looks/backups/Shader>).GetAttribute('outputs:out')]
2022-08-16 00:49:21  [Error] [asyncio] ...\asyncio\base_events.py:1619] Task exception was never retrieved
2022-08-16 00:49:21  [Error] [asyncio] future: <Task finished coro=<StageManager.draw_label_on_stage_async() done, defined at exception=ErrorException(Error in 'pxrInternal_v0_20__pxrReserved__::UsdStage::_SetValueImpl' at line 6028 in file C:\b\w\ca6c508eae419cf8\USD\pxr\usd\usd\stage.cpp : 'Empty typeName for </Looks/backups/Shader.inputs:diffuse_texture>')>
2022-08-16 00:49:21  [Error] [asyncio] Traceback (most recent call last):
2022-08-16 00:49:21  [Error] [asyncio]   File "line 247, in draw_stage_async
2022-08-16 00:49:21  [Error] [asyncio]     texture.Set(str(output_file))
2022-08-16 00:49:21  [Error] [asyncio] pxr.Tf.ErrorException: 

It’s waiting for and getting the shader now, but it doesnt have that Attribute:
If I print them out:

2022-08-16 00:49:21  [Info] [omni.kit.app.impl] [py stdout]: [Usd.Prim(</Looks/default/Shader>).GetAttribute('info:id'), Usd.Prim(</Looks/default/Shader>).GetAttribute('info:implementationSource'), Usd.Prim(</Looks/default/Shader>).GetAttribute('info:mdl:sourceAsset'), Usd.Prim(</Looks/default/Shader>).GetAttribute('info:mdl:sourceAsset:subIdentifier'), Usd.Prim(</Looks/default/Shader>).GetAttribute('inputs:excludeFromWhiteMode'), Usd.Prim(</Looks/default/Shader>).GetAttribute('outputs:out')]

Should I call CreateAttribute(“inputs:diffuse_texture”) ? I think it wants a type as well

Turns out the answer for this is in the omni.usd docs in Code. The problem is that attributes are lazily created on the shader prim, but selection the prim populates them. I also had success with creating the needed attribute with CreateAttribute instead of changing the selection, but it didn’t work for Gavin. I updated the Solution accordingly.

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