Is it possible to set any attribute by strings: path, value, usd-type?

I have an omniverse project. From outside the application I get a json which tells me what attributes to change in what prim.

For example json can contain a string like
"xformOp:translate" : "(1, 2, 3)"
where (1,2,3) is a vector of xyz coords that need to be applied to a certain element.

I know I can use

myPrim.GetAttribute(TfToken("xformOp:translate")).Set(GfVec3d(1,2,3));

But I want to do this abstractly like

string attr = jsonString["attrName"]
string attrValue = jsonString["value"]
myPrim.GetAttribute(TfToken(attr)).Set(attrValue);

In other words, I want usd to automatically convert my string attrValue to the desired type of attribute. Maybe it somehow can be inferred from the attribute itself.

can I do this?

Going off of the USD API itself, then as far as I know all you can really do is abstract it away into your own function that is capable of doing the conversions where needed.

def set_attr(attr: Usd.Attribute, value: object):
    attr_type = attr.GetTypeName()   
    if attr_type == "x":
        # do x
        attr.Set(converted_value)
    elif attr_type == "y":
        # do y
        attr.Set(converted_value)
    else:
        # fallback to default behavior
        attr.Set(value)

Whether omniverse has any wrapping API layer over that which does that for you - I don’t know.

Not sure about the C++ side of things, but in Python you can do this:

USD API: Usd.Attribute.GetTypeName().type.pythonClass([(0,1,2)]) 
SDF API: Sdf.AttributeSpec.typeName.type.pythonClass([(0,1,2)])

You can find some more examples here:

If the type is an implicitly converted fundamental type (like float/double/int),
you’ll have to get the type from the default value.

from pxr import Sdf
value_type_name = Sdf.ValueTypeNames.TexCoord2dArray
# value_type_name = Sdf.ValueTypeNames.Color3d
# value_type_name = Sdf.ValueTypeNames.Vector3dArray
# value_type_name = Sdf.ValueTypeNames.Bool
# Or from an existing atts
# value_type_name = attr.GetTypeName()

### Type (Actual type definiton, holds data about container format
### like C++ type name and the Python class
value_type = value_type_name.type
print(value_type) # Returns: Tf.Type.FindByName('VtArray<GfVec2f>')
print(value_type, value_type.pythonClass, value_type_name.defaultValue.__class__) # Returns: Tf.Type.FindByName('VtArray<GfVec2d>') <class 'pxr.Vt.Vec2dArray'> <class 'pxr.Vt.Vec2dArray'>
### Get the Python Class
cls = value_type.pythonClass
# Or (for base types like float, int, string, token)
if not cls:
    default_value = value_type_name.defaultValue
    cls = default_value.__class__
instance = cls()

Note that it won’t convert things like strings to arrays (unless the type constructor supports it, which I haven’t seen so far).

You also don’t need to do this for most types ( I think) if you don’t need access to the type class, as it is implicitly converted.

2 Likes