All of shaderfx in maya is organized by a single, undocumented command. Which is pretty lame.
However, it’s not as bad as it seems once you figure out the standard command form, which is always some variant of this form:
shaderfx -sfxnode <shader node> -command <command> <node id>
sfxnode
argument tells maya which sfx shader to work on. The command
flag indiciates an action and the node id
specifies an node in the network. Nodes are assigned an id in order of creation, with the firstnode after the root ordinarily being number 2 and so on – however the ids are not recycled so a network which has been edited extensively can have what look like random ids and there is no guarantee that the nodes will form a neat, continuous order. Many commands take additional arguments as well. Those extra always follow the main command; thus
shaderfx -n "StingrayPBS1" -edit_int 19 "uiorder" 1;
uiorder
field on node 19 to a value of 1. The
shaderfx
command can also return a value: to query the uiorder
field in the example above you’d issue shaderfx -n "StingrayPBS1" -getPropertyValue 19 "uiorder"; // Result: 1 //
So, the good news is that the
shaderfx
command is actually pretty capable: so far, at least, I have not found anything I really needed to do that the command did not support. For some reason the help documentation on the mel command is pretty sparse but the python version of the help text is actually quite verbose and useful.Still, it’s kind of a wonky API: a single command for everything, and no way to really reason over a network as a whole. Worse, the different types of nodes are identified only by cryptic (and undocumented) numeric codes: for example a
Cosine
node is 20205 – but the only way to find that out is to use the getNodeTypeByClassName
command (and, by the way, the node type names are case and space sensitive).With all that baggage I was pretty discouraged about actually getting any work done using shaderfx programmatically. However a little poking around produced what I hope is a somewhat more logical API, which I’m Cleanup crewsharing on github.
The
sfx
module is a plain python module - you can drop it into whatever location you use to story your Maya python scripts. It exposes two main classes:SFXNetwork represents a single shader network – it is a wrapper around the Maya shader ball. The
SFXNetwork
contains an indexed list of all the nodes in the network and also exposes methods for adding, deleting, finding and connecting the nodes in the network.SFXNode represets a single node inside the network. It exposes the properties of the node so they can be accessed and edited using python dot-style syntax.
The module also includes to submodules,
sfxnodes
and pbsnodes
. These make it easier to work with the zillions of custom node ids: Instead of remembering that a Cosine
node is type 20205, you reference sfxnodes.Cosine
. I’ll be using the StingrayPBSNetwork
class and the pbsnodes
submodule in my examples, since most of my actual use-case involves the Stingray PBS shader. The syntax and usage, however, are the same for the vanilla SFXNetwork
and sfxnodes
– only the array of node types and their properties.Here’s a bit of the basic network functionality.
To create a new shaderfx network, use the Create a network
create
classmethod:from sfx import StingrayPBSNetwork import sfx.pbsnodes as pbsnodes network = StingrayPBSNetwork.create('new_shader')
An SFXNetwork contains a dictionary of id and nodes in the field Listing nodes
nodes
. This represents all of the graph nodes in the network. Note I’ve used a different shader than the default one in this example to make things easier to read.print network.nodes # { 1 : <sfxNode UnlitBase (1)>, 2: <sfxNode 'MaterialVariable' (2)> } print network.nodes[2]: # <sfxNode 'MaterialVariable' (2)>
show node IDs
toggle in the ShaderFX window.The values of the node dictionary are
SFXNode
objects.To add a node to the network use its Adding new nodes
add()
method and pass a class from either the sfxnodes
or pbsnodes
submodule to indicate the type. if_node = network.add(pbsnodes.If) # creates an If node and adds it to the network var_node = network.add(pbsnodes.MaterialVariable) # creates a MaterialVariable node and adds it to the network
Connecting nodes in shaderfx requires specifying the source node the source plug, the target node and the target plug. Unforunately the plugs are indentifited by zero-based index numbers: the only way to know them by default is to count the slots in the actual shaderfx UI. Output plugs are usually (not always) going to be index zero but the target plugs can be all over the map. Connecting nodes
To make this cleaner, each
SFXNode
object exposes two fields called inputs
and outputs
, which have named members for the available plugs. So to connect the ‘result’ output of the var_node
object to the input named ‘B’ on the if_node
:network.connect(var_node.outputs.result, if_node.inputs.b)
MayaCommandError
will be raised.It’s common to have to ‘swizzle’ the connections: to connect the x and z channels of a 3-pronged output to channels of an input, for example. Mismatched swizzles are a common cause of those
MayaCommandErrores
. You can set the swizzle along with the connection by passing the swizzle you need as a stringnetwork.connect(var_node.outputs.result, if_node.inputs.b, 'z') # connects the 'x' output of var_node to the b channel of the input
Nodes often have editable properties. There are a lot of different ones so it is often necessary to inspect a node and find out what properties it has and what type of values those properties accept. Every Setting node properties
SFXNode
object has a read-only member properties
, which is a dictionary of names and property types. Using the same example objects as above:print if_node.properties ### BLah blah example here
node = network.properties[5] # get the node at index 5 in this network print node.properties: # { 'min': 'float', 'max': 'float', 'method': 'stringlist' } print node.min # 1.0 # getting a named property returns its value. node.min = 2.0 # sets the node value print node.min # 2.0
print node.i_dont_exist # AttributeError: no attribute named i_dont_exist node.i_dont_exist = 99 # MayaCommandError
No comments:
Post a Comment