Working with Group Attributes

GroupAttribute instances are generally created indirectly using the GroupBuilder helper class.

To create a new group attribute, first instantiate a GroupBuilder. You may then mutate the builder using various operations, for example adding (name, attribute) pairs to it using the set() method, or deleting an existing attribute by passing its name to del(). When done, retrieve the newly constructed GroupAttribute with the GroupBuilder’s build() method.

It's also possible to update a GroupBuilder with the contents of an existing group attribute using the update() and deepUpdate() methods. The update() method performs a shallow merge, where existing attributes are overwritten if they share the same name as the new attributes. A common pattern is to call update() on an empty builder to pre-populate it with the contents of an existing group attribute. The deepUpdate() method is the recursive version of update(), effectively overlaying the contents of the incoming groups atop the existing groups of the builder.

As a convenience, GroupBuilder supports creating arbitrarily nested attribute structures by passing a dot-delimited path to its set() and del() methods. (This implies that the . (period) character is not a valid attribute name!)

Note:  Unlike the types mentioned so far, GroupBuilder is not itself an Attribute.

The code snippets listed below show the creation of a group attribute with the following structure:

    {       "my": {         "nested": {           "attribute": IntAttribute(2)         }       },       "myTopLevelAttribute": StringAttribute("taco"),       "myOtherTopLevelAttribute": FloatAttribute(4.0f)     }

C++

 GroupBuilder gb;     gb.set("my.nested.attribute", IntAttribute(2));     gb.set("myTopLevelAttribute", StringAttribute("taco"));     gb.set("myOtherTopLevelAttribute", FloatAttribute(4.0f));       GroupAttribute groupAttribute = gb.build();     // |groupAttribute| now has the structure listed above; |gb| is empty.

Python

    gb = GroupBuilder()     gb.set("my.nested.attribute", IntAttribute(2))     gb.set("myTopLevelAttribute", StringAttribute("taco"))     gb.set("myOtherTopLevelAttribute", FloatAttribute(4.0))       groupAttribute = gb.build()     # |groupAttribute| now has the structure listed above; |gb| is empty.

Lua (OpScript)

    local gb = GroupBuilder()     gb:set("my.nested.attribute", IntAttribute(2))     gb:set("myTopLevelAttribute", StringAttribute("taco"))     gb:set("myOtherTopLevelAttribute", FloatAttribute(4.0))       local groupAttribute = gb:build()     -- |groupAttribute| now has the structure listed above; |gb| is empty.

Notes

There is no way to inspect the contents of the builder while you are mutating it. Instead, you must call build() and inspect the generated GroupAttribute. Note that, by default, calling build() clears the contents of the builder, and to override this behavior you must pass the constant GroupBuilder::BuildAndRetain (C++), GroupBuilderBuildAndRetain (Python), or GroupBuilder.BuilderBuildMode.BuildAndRetain (Lua) to build().

Note on Backwards Compatibility

Previous versions of Katana would retain the contents of the builder when calling build(). Customers with existing C++ plug-ins they wish to use in Katana 2.0v1, and after, are advised to audit their uses of GroupBuilder::build() to ensure the new semantics do not cause unintended side effects.

Group Inheritance and the groupInherit Flag

Group attributes have a special flag called groupInherit. Setting this flag to True signals to Katana that all attributes contained in the group should be inherited by child locations.

By default, group attributes created by GroupBuilder have their groupInherit flag set to true. To disable this, call setGroupInherit(false) (C++, Lua) or setGroupInherit(False) (Python) on the builder. This groupInherit flag on the builder is "sticky": setGroupInherit() may only be called once per builder, and subsequent calls have no effect.

As the name suggests, group inheritance is a property of the group and not the data attributes contained within it. The use of NullAttribute in combination with group inheritance allows for fine-grained control over which attributes are inherited by child locations. Specifically, Katana interprets a null attribute in a group as a signal to ignore inherited values for the attribute with the given name, forcing the default value if available. (If no default value is available the attribute is simply deleted.)

Inheritance Rules for Attributes

By default, attributes are inherited from parent locations. However, attributes can be overwritten at specified locations where the values differ from ones defined higher up in the hierarchy, as used for Light Linking.

Some attributes are not inherited, for instance the globalStatements of a renderer defined at /root or the globals defined at /root/world. Another example is the xform attribute, where it would not make sense to inherit a transform defined for a group to all its children and thus perform the operation multiple times.

Setting Group Inheritance using the API

To prevent an attribute from being inherited, use the API function setGroupInherit() to disable group inheritance. For example:

FnKat::GroupBuilder gb; gb.setGroupInherit(false); gb.build();