Continuing our bottom-up look at Gaffer from a pipeline perspective, in this instalment we look at the fundamentals of how to Gaffer is configured. We will cover:
- Environment variables
- Settings and Preferences
- Gaffer’s Metadata system
There are numerous environment variables that Gaffer uses to modify its behaviour. Generally, these are used to locate additional resources (or toggle legacy compatibility modes) rather than for more general settings or preferences. Here are some of the ones you may need to set as part of integrating Gaffer into a pipeline:
- GAFFER_STARTUP_PATHS A right-to-left ordered path from which startup scripts are loaded (see this post).
- ARNOLD_ROOT The root of the Arnold installation to use*.
- DELIGHT The root of a 3Delight installation to use*.
- OSL_SHADER_PATHS A path-style list of directories to search for OSL shaders for use with compatible renderers.
- GAFFER_REFERENCE_PATHS A path-style list of directories to search for relative
.grfpaths. Searched left-to-right.
- OCIO The path to an
.ocioconfig file to be used by Gaffer.
*These need to be ABI compatible with the versions Gaffer was built against. See the Gaffer release notes for more on which version was used.
Additional env vars can be used to register custom code or for development purposes:
- PYTHONPATH Behaves as normal, affecting all python code run in Gaffer.
- GAFFER_IMAGE_PATHS A path-style list of directories to search for images specified in UI code for icons/etc…
- GAFFER_EXTENSION_PATHS A path-style list of directories containing ‘gaffer’ style distributions of additional code. This adds the following sub-directories to the appropriate paths for run-time use:
- GAFFER_DEBUG When set to
gafferwill run via a c++ debugger. GAFFER_DEBUGGER can also be set to some alternate tool if the default
Settings and Preferences
Gaffer doesn’t store its configuration in separate preference files (such as XML or json). Instead settings are either stored either in plugs on nodes within a script (for per-script values), or in memory (for global configuration).
- An example script setting, stored on the script node, would be the timeline frame range.
- Example in-memory settings would be a bookmark for the file browser or a new image size preset that you wish to be available across all scripts.
In the first part of this series we looked at Gaffer’s startup mechanism. This gives us a convenient way to run arbitrary python code when applications or Gaffer libraries load. We use this to set in-memory settings each time an application starts.
Like most things in Gaffer, changing settings are just python calls to an API. This approach achieves the same result as other tools do with preference files, but without the need to have two different mechanisms/formats to set values. You can test the config code in the Python Editor and then move it to a startup file.
Before we get onto specific examples in the next instalment, it’s first important to understand the concept of the metadata system in Gaffer.
Some customisations are made by direct API calls to various gaffer sub-systems. Such as adding a bookmark via
GafferUI.Bookmarks. Many however, are achieved by manipulating values in Gaffer’s metadata system.
This is a simple key/value registry that allows arbitrary data to be associated with:
- A Gaffer class (eg: a node or plug type)
- An instance of a Gaffer graph class (eg: a specific plug on a specific node)
- A path to a child of a Gaffer class (eg: a named plug on all nodes of a given type)
- Arbitrary strings.
There are many well-known keys that are used by Gaffer (more on which later) but you can use others within your own tools if required. In the Gaffer UI, metadata controls amongst other things:
- UI colors and appearance.
- Node annotations.
- Default values and UI widgets for plugs.
The following sets the color for all Transform nodes, by registering a value for the node class itself. This will affect new and existing nodes:
Gaffer.Metadata.registerValue( GafferScene.Transform, "nodeGadget:color", imath.Color3f( 0.485, 0.7112, 0.2255 ) )
Swapping the registration to a specific node instance instead limits the change to that specific node and no others:
Gaffer.Metadata.registerValue( root["Transform2"], "nodeGadget:color", imath.Color3f( 1, 0, 0 ) )
Coarsely speaking, when looking up a value for instances, the metadata system first looks for a value for the supplied object, if none is found, it then looks up the value for its class.
There is another variation where metadata can be registered with the path to a child, for example a named plug on a specific node type:
Gaffer.Metadata.registerValue( GafferImage.Catalogue, "directory", "userDefault", "$HOME/catalog" )
By default, metadata associated with instances in the Node Graph is persisted when the script is saved, so it will be available when it is re-opened. This is not true of data associated with classes so those changes need to be made in startup scripts if required for future sessions.
Gaffer metadata is stored independently. Code that is ‘metadata aware’ simply looks up any entries for a particular object as needed to determine the correct behaviour.
In future posts, we’ll look at common customisations that can be made using the above mechanisms.