AttributeScript to OpScript Conversion

The following Python function examples for AttributeScript have Lua function examples for use with OpScript. This is not an exhaustive list, but may offer a helpful "cheat sheet" for those with AttributeScripts in existing scenes who may wish to convert over to OpScript.

For more information on OpScript, please refer to Working with Attributes, or look at the OpScript tutorials in the Help > Example Projects menu in Katana. For details on the OpScript API, see the OpScript Reference documentation available from the Help > Documentation menu in Katana.

GetAttr

Locally Set Values

Python:

tacoList = GetAttr("taco")  # local value	 
if tacoList: 
  tacoSingleValue = tacoList[0]  # returns None if not available locally

Lua:

-- local value
local tacoAttr = Interface.GetAttr("taco")
if tacoAttr then
  -- Convert to a table/list by calling the getNearestSample method
  local tacoList = tacoAttr:getNearestSample(0.0)
  -- First element (Note that Lua table indices start at 1)
  local tacoSingleValue = tacoList[1]
end

If you just want the first value at time 0.0, there's a convenient function for that in Lua:

-- local value
local tacoAttr = Interface.GetAttr("taco")
if tacoAttr then
  local tacoSingleValue = tacoAttr:getValue()
end

Inherited Values

Python:

# inherited value. GetAttr() returns None if not available globally or locally
			
tacoInheritedList = GetAttr("taco", inherit=True)
if tacoInheritedList:
  tacoInheritedValue = tacoInheritedList[0]

Lua:

-- inherited value
local tacoInheritedAttr = Interface.GetGlobalAttr("taco")
if tacoInheritedAttr then
  -- Convert to a table/list by calling getNearestSample()
  local tacoInheritedList = tacoInheritedAttr:getNearestSample(0.0)
  -- First index (Note the 1, not the 0)
  local tacoInheritedValue = tacoInheritedList[1]
end

If you just want the first value at time 0.0, there's a convenient method for that in OpScript:

-- inherited value
local tacoInheritedAttr = Interface.GetGlobalAttr("taco")
if tacoInheritedAttr then
  local tacoInheritedValue = tacoInheritedAttr:getValue()
end

Specifying a Location

Python:

tacoAttrAtLocation = GetAttr('taco', atLocation='/root/whatever')

Lua:

local tacoAttrAtLocation = Interface.GetAttr('taco', '/root/whatever')

Additional Inputs

With Python, AttributeScripts were limited to 1 input, so you couldn't add more inputs to a node. OpScript removes this restriction: OpScript nodes have a Display as multi­ input option that can be enabled. The OpScript is run on each location matches by its CEL expression, as evaluated in the left­-most input (usually named "i0"). If you want to read an attribute from an additional input, you can specify the scenegraph location and input index:

-- Read from second input (for example, "i1").
local tacoAttrFromSecondInput = Interface.GetAttr('taco', '/root/whatever', 1)

SetAttr

Python:

SetAttr("type", ["subdmesh"])

Lua:

Interface.SetAttr("type", StringAttribute("subdmesh"))

Unlike AttributeScript, there is no implicit conversion from primitive types like strings and numbers (or lists thereof) to their corresponding Attribute types. OpScript requires that you construct an Attribute instance explicitly.

Listed below are the constructors for the various Attribute sub-classes:

  • StringAttribute(string)
  • FloatAttribute(number)
  • DoubleAttribute(number)
  • IntAttribute(number)
  • GroupAttribute()
  • NullAttribute()

Note:  To delete an attribute in Lua, use the DeleteAttr() function provided by the Interface module.

Deleting an Attribute

To delete an attribute in Lua, use the DeleteAttr() functions provided by the Interface module.

Python:

SetAttr("taco") # setting it to "nothing" deletes it in python

Lua:

Interface.DeleteAttr("taco")

GetName, GetFullName

Python:

name = GetName()
fullname = GetFullName()

Lua:

local name = Interface.GetOutputName()
local fullname = Interface.GetOutputLocationPath()

XForm Manipulation

local xformGroup = Interface.GetGlobalXFormGroup()
local xformMatrix = XFormUtils.CalcTransformMatrixAtTime(xformGroup, 0.0)

Util.AssetResolve

Python:

resolvedAsset = Util.AssetResolve("foo://bar")

Lua:

local assetPlugin = Asset.GetDefaultAssetPlugin()
local resolvedAsset = assetPlugin:resolveAsset("foo://bar")

User Parameters

Python:

tacoList = user.taco
tacoSingleValue = tacoList[0]

Lua:

local tacoParam = Interface.GetOpArg("user.taco")
if tacoParam then
  local tacoList = tacoParam:getNearestSample(0.0)
  local tacoSingleValue = tacoParam:getValue()
end

Language Basics

print()

Python:

print "tacoSingleValue", tacoSingleValue
print "tacoList", tacoList

Lua:

print("tacoSingleValue", tacoSingleValue)

-- if you have an 'array-like' table with consecutive indices
for i, value in ipairs(tacoList) do
  print("tacoList[" .. i .. "]", value)
end

-- if you have a 'dictionary-like' table. Note that the order of iteration is undefined, much like Python

for key, value in pairs (tacoDict) do
  print("tacoDict[" .. key .. "]", value)
end

dir()

The dir() function in Python lets you introspect an object (or the current local scope), which can be useful for seeing what tools you have at your disposal. Lua doesn't have a precise equivalent, but any table can be iterated over using the pairs() function, so you can explore a lot in a similar way.

Python:

dir()
dir(some_object)

Lua:

local keys={}
for key, value in pairs(Interface) do 
  table.insert(keys,key) 
end
			
table.sort(keys)

for index, value in ipairs(keys) do 
  print(value)
end

Comments

Python:

# This is a single line comment

Lua:

-- This is a single-line comment
--[[This is a
  multi-line
  comment]]

String Concatenation

Python:

tacoConcat = "taco1" + "taco2"

Lua:

local tacoConcat = "taco1" .. "taco2"

String Manipulation

Most of the string operations you would do in Python are available in OpScript as part of the pystring module. For instance to split a string and get the last token, follow the example below.

Python:

path = "/foo/bar/baz"
lastToken = path.split('/')[-1]

Lua, using pystring:

local path = "/foo/bar/baz"
local pathTokens = pystring.split(path, "/")
local lastToken = pathTokens[#pathTokens]

Lua, the "native" way:

local path = "/foo/bar/baz"
local lastToken = nil
-- string.gmatch returns an iterator of matches
for token in string.gmatch(path, "[^/]+") do
  lastToken = token
end

Slicing

Lua, the "native" way to slice a table requires a function:

local function slice(values, i1, i2)
  local n = #values
  -- default values for range
  i1 = i1 or 1
  i2 = i2 or n
  if i2 < 0 then
    i2 = n + i2 + 1
  elseif i2 > n then
    i2 = n
  end
  if i1 < 1 or i1 > n then
    return {}
  end

  local result = {}
  for i = i1,i2 do
    table.insert(result, values[i])
  end
  return result
end

local path = "/foo/bar/baz"
local pathTokens = pystring.split(path, "/")
pathTokens = slice(pathTokens, 1, -2)

local path = pystring.join("/", pathTokens)

math library

The math library is already loaded, by default. You can just use its functions, which are mostly the same as in Python. Examples include:

  • math.sqrt()
  • math.abs()
  • math.sin()

Note:  The official documentation of the math library can be found at http://www.lua.org/manual/5.1/manual.html#5.6.
Example usage of the math library can be found at http://lua-users.org/wiki/MathLibraryTutorial.

Arithmetic

For arithmetic, you use mostly the same operators as in Python (+, -, *, /, %).

An exception is power:

Python:

x**y

Lua:

x^y

You can alternately use the math library:

math.pow(x,y)

Executing External Scripts

Like execfile in AttributeScript, dofile in OpScript caches the compiled code for each file executed so that cooking thousands of locations on the render farm does not bring down the network.

Python:

execfile("/my/external/attribute_script.py")

Lua:

dofile("/my/external/op_script.lua")

Caveats

Be aware that module functions that directly map to existing C++ APIs use zero­-based indices, even though Lua indices generally start at 1. This is done to make code as portable as possible between Lua and C++. For functions that return native Lua tables or list tables (such as IntAttribute:getNearestSample(0.0)), the return values are indexed in the Lua standard way, starting at 1.

Lua:

-- getChildName index argument is zero-based
for i=0, groupAttr:getNumberOfChildren() - 1 do
  print(i .. ': ' .. groupAttr:getChildName(i))
end

-- getNereastSample returns a table that uses one-based indices, as is typical
-- in Lua.
local attr = IntAttribute({1, 2, 3})
local values = attr:getNearestSample(0.0)

-- Prints "1: 1", "2: 2", "3: 3"
for index, value in ipairs(values) do
  print(index .. ': ' .. value)
end