OpScript Update AOVs

If you are new to Lua for Katana or Lua in general, the foundry has a great set of videos here , also this video by Trixter Film. And as always check out the developer guides for more information.

With that out of the way. I was messing around in Katana trying to figure out workflow ideas and I came up with an opscript to force the lights in your gaffer to match based on the names of the parent rigs. Why would you want this? Mostly as a fail safe. In a perfect world when you create lights you should be setting the AOVs but sometimes we forget. Or during setup we reorganize the gaffer and forget to go in and rename things. Or you name it but there’s a spelling mistake and then its not showing up as expected inside your compositing software.
Here’s a break down of my opscript to solve some of those problems. Hope you find it useful!

*This is using the Arnold render engine. the attribute names for the lights aovs will be different based on your render engine.

--::::::::::::::::::::::::::::    
--:::: Light Annotations ::::
--::::::::::::::::::::::::::::    
-- Robert Matier 
-- Version: 1..0
-- Last Updated: 2025/06/02

--:::: UPDATES 

-- Katana OpScript designed to force light aovs to predefined patterns
-- Works by running through all the elements of type 'light' under the main rig 
-- Checks the AOVs and potetntially reassigns the values based on parent rig names
-- checkbox to enable annotations to see the changes
--limitations : requires working in the specific predefined workflow
--

-- variable setup
local enableAnno = Interface.GetOpArg('user.enableAnnotation')
local enableAnnoVal = math.floor(enableAnno:getValue())

local approvedAovNames = {"LG_A","LG_B","LG_C","LG_D","LG_E","LG_F"} --adjust as needed
local search = 'LG_'
local lgRigFull = Interface.GetInputLocationPath()
local type = Interface.GetInputLocationType(lgRigFull)
local lg = Interface.GetAttr('material.arnoldLightParams.aov')
local index = pystring.find(lgRigFull, search) -- looking for LG_ in path

--logic
if index > 0 then -- the light is under a parent rig with the correct naming
    if lg then -- the aov isn't nil         
        local checkLg = string.sub(lgRigFull, index +4, index +4) --we are looking for 'LG_*' and then getting the 4th character lua starts a t 1 not 0
        local lgVal = lg:getValue()
    
        if lgVal == checkLg then -- name matches the parent rig name
            local lgVal = lg:getValue()
            Interface.SetAttr("viewer.default.annotation.text", lg)
            Interface.SetAttr('viewer.default.annotation.color', FloatAttribute({0.0,1.0,0.0}))
    
        elseif lgVal ~= checkLg then -- name doesn't match the parent rig
            local message = 'aov changed from ' .. lgVal .. ' to ' .. checkLg
            Interface.SetAttr("viewer.default.annotation.text", StringAttribute(message))
            Interface.SetAttr('viewer.default.annotation.color', FloatAttribute({1.0,1.0,0.0}))
        end
    else       
        local index = pystring.find(lgRigFull, search) -- looking for LG_ in path
        if index > 0 then --there is no local aov assigned. will use parent rig name 
            local rigLg = string.sub(lgRigFull, index +4, index +4) 
            local message = 'aov not set using heirachy ' .. rigLg
            Interface.SetAttr("viewer.default.annotation.text", StringAttribute(message))
            Interface.SetAttr('viewer.default.annotation.color', FloatAttribute({1.0,0.5,0.0}))
        end
    end
else 
    if lg then --the aov isn't nil               
        local lgVal = lg:getValue()
        local message = 'not in heirachy using local aov ' .. lgVal
        Interface.SetAttr("viewer.default.annotation.text", StringAttribute(message))
        Interface.SetAttr('viewer.default.annotation.color', FloatAttribute({1.0,0.0,0.0}))
    else --there is no local aov     
        local message = 'NO AOV ASSIGNED'
        Interface.SetAttr("viewer.default.annotation.text", StringAttribute(message))
        Interface.SetAttr('viewer.default.annotation.color', FloatAttribute({0.0,0.0,0.0}))

    end

end

if enableAnnoVal == 0 then
    Interface.SetAttr("viewer.default.annotation.text", nil)
    Interface.SetAttr("viewer.default.annotation.color", nil)
end

If everything goes as expected we should be seeing something like this.
Green - AOV existed and matched the Lettered Rig location - using local AOV value
Yellow - AOV existed but did not match the Lettered Rig location - AOV gets updated to Lettered Rig
Orange - AOV did not exist but Lettered Rig location exists - AOV gets updated to Lettered Rig
Red - AOV existed but not under a Lettered Rig - using local AOV value
Black - AOV did not exist and Lettered Rig location does not exist - no action

NOT DISPLAYED THE USER CHECKBOX TO TOGGLE THE ANNOTATIONS ON



The way I have it setup we are expecting aovs that are a single capital letter (A,B,C,…,Z ; technically in the example i only go up to F in the approved names) these aovs should live under a rig that follows the naming convention ‘LG_’ and then a capital letter. They may be nested inside several rigs but eventually in the hierarchy they should have one of those Lettered Rigs as a parent. With this setup in mind we can check the aov attributes and compare that to the scene graph location.

Previous
Previous

Floyd–Steinberg dithering

Next
Next

Slit-scan