Extrait de code Guerilla
- Documentation officielle de l’API Python
- Utilisation de la console
- Contexte de modification
- Récupérer le type d’un nœud depuis la UI
- Récupérer un nœud depuis son nom/chemin
- Information sur un nœud
- Mettre un nœud à l’intérieur d’un autre
- Itérer à travers les RenderPass/Layer/AOV
- Récupérer et définir des valeurs d’attributs
- Activer et désactiver "Use Project Settings" d’une RenderPass
- Savoir si deux nœuds sont connectés, dans n’importe quel ordre
- Itérer à travers tous les nœuds entrant dans un nœud donné
- Itérer à travers les nœuds à la gauche d’un nœud donné
- Connecter un nœud entre deux autres
- Frame (ressemblant au backdrop Nuke)
- Créer une macro à l’intérieur d’un RenderGraph
- Créer un sub shader
- Savoir si un nœud a un attribut d’entrée ou de sorti avec le nom donné
- Récupérer le nœud connecté à l’attribut donné d’un nœud donné
- Créer le nœud de RenderGraph par défaut
- Savoir si un RenderGraphNode est connecté à la sortie d’un RenderGraph
- Color range en SL
- World space AABB
- Scene
- Récupérer les informations de primitive (face count, vertex count, etc.)
Documentation officielle de l’API Python
Il y a quelques exemples dans la section Examples de la documentation officielle de l’API Python.
Utilisation de la console
La console Guerilla est très utile. N’hésitez pas à l’utiliser !
Ouvrez-la depuis "View"/"Show/Hide console" :
Une fois dans la console, vous pouvez créer un onglet Python via File/New/Python ou directement via le raccourci clavier Ctrl+Shift+N. Vous pouvez aussi sauvegarder votre fichier Python pour le conserver pour la prochaine session.
Assurez-vous que l’option "View"/"Command Echo" est activée de sorte que Guerilla affiche les commandes (lua) qu’il exécute quand vous travaillez depuis la UI (à la Maya). Cela permet de récupérer les noms des attributs que vous modifiez (plugs) plus facilement.
Par exemple, quand vous changer la valeur de l’attribut Filter (dans la section Pixel Filter d’une RenderPass
), vous verrez :
local mod=Document:modify()
mod.set(_"RenderPass.PixelFilter","triangle")
mod.finish()
C’est le code lua exécuté par Guerilla. Il crée un modificateur mod
, l’utilise pour modifier la valeur et le « ferme ». « _
» est un alias de pynode()
.
En Python, un tel code devient :
import guerilla
with guerilla.Modifier() as mod:
guerilla.pynode("RenderPass.PixelFilter").set("triangle")
# ou
guerilla.pynode("RenderPass").PixelFilter.set("triangle")
# ou encore
guerilla.Document().RenderPass.PixelFilter.set("triangle")
Les trois lignes font la même chose.
Contexte de modification
Comme vous avez pu le remarquer dans la section précédente, Guerilla a besoin d’un contexte de modification pour modifier les nœuds correctement :
local mod=Document:modify()
-- do some stuff
mod.finish()
L’API Python s’appuie sur l’instruction with
et le contexte guerilla.Modifier()
. Cela veut dire que l’équivalent Python au code lua ci-dessus est :
with guerilla.Modifier() as mod:
# do some stuff
Cela a l’inconvénient d’utiliser une tabulation. Vous pouvez tricher en utilisant directement les méthodes du contexte :
mod = guerilla.Modifier()
mod.__enter__()
# do some stuff
mod.__exit__(None, None, None)
Plus d’information disponible ici.
Récupérer le type d’un nœud depuis la UI
Vous pouvez obtenir le type d’un nœud depuis l’onglet Node List de Guerilla. Le type du nœud est affiché à droite de son nom :
Ici, on voit que Beauty
est un nœud de type LayoutOut
.
Récupérer un nœud depuis son nom/chemin
Guerilla fournie deux façons de récupérer un nœud depuis son nom (ou son chemin) :
guerilla.pynode("Toto")
guerilla._("Toto")
Les deux renvoient une instance du nœud Toto
. La différence étant que le second renvoie None
si le nœud n’existe pas, alors que le premier lève une exception de type ValueError
.
Information sur un nœud
Afficher des informations sur le nœud sélectionné :
import guerilla
# reference vers le document Guerilla
doc = guerilla.Document()
for node in doc.selection():
print "Type:", type(node)
print "Name:", node.name
print "Path:", node.path
print "Parent's name:", node.parent.name
print "Plugs:"
for plug in node.plugs():
print " ", plug.name
Mettre un nœud à l’intérieur d’un autre
Ce code met le nœud a
à l’intérieur du nœud b
:
with guerilla.Modifier() as mod:
a.move(b)
Assurez-vous que a
est déconnecté des autres nœuds avant de le déplacer.
Itérer à travers les RenderPass/Layer/AOV
import guerilla
doc = guerilla.Document()
for render_pass in doc.children(type='RenderPass'):
print render_pass.name
for layer in render_pass.children(type='RenderLayer'):
print layer.name
for aov in layer.children(type='LayerOut'):
# le nom affiché de l'aov est différent du nom du noeud
print aov.name, aov.PlugName.get()
Sur une nouvelle scène, vous aurez :
RenderPass
Layer
Input1 Beauty
Récupérer et définir des valeurs d’attributs
In Guerilla, node attributes are of type guerilla.Plug
and you get/set values using get()
and set()
method.
Dans Guerilla, les attributs des nœuds sont de type guerilla.Plug
(documentation disponible ici) et vous Récupérez et définissez leur valeur en utilisant respectivement les méthodes get()
et set()
.
Changer la résolution globale
import guerilla
doc = guerilla.Document()
with guerilla.Modifier() as mod:
doc.ProjectWidth.set(320)
doc.ProjectHeight.set(240)
Vous pouvez diviser la résolution par deux de cette façon :
import guerilla
doc = guerilla.Document()
with guerilla.Modifier() as mod:
doc.ProjectWidth.set(doc.ProjectWidth.get()/2)
doc.ProjectHeight.set(doc.ProjectHeight.get()/2)
Activer et désactiver "Use Project Settings" d’une RenderPass
Quand vous cliquez sur la case à cocher Use Project Settings d’une RenderPass
, Guerilla quelques opérations sous le capot :
import guerilla
doc = guerilla.Document()
# part du principe que vous avez selectionne une RenderPass
rp = doc.selection()[0]
# deconnecte les valeurs de Project Settings
with guerilla.Modifier() as mod:
rp.Width.disconnect(doc.ProjectWidth)
rp.Height.disconnect(doc.ProjectHeight)
rp.AspectRatio.disconnect(doc.ProjectAspectRatio)
# reconnecte les valeurs a Project Settings
with guerilla.Modifier() as mod:
rp.Width.connect(doc.ProjectWidth)
rp.Height.connect(doc.ProjectHeight)
rp.AspectRatio.connect(doc.ProjectAspectRatio)
Savoir si deux nœuds sont connectés, dans n’importe quel ordre
def are_connected(a, b):
# on teste si les noeuds sont connecte dans un sens...
for out in a.getoutputs():
for in_ in b.getinputs():
if in_.Plug.isconnected(out.Plug):
return True
# ...et dans l'autre
for out in b.getoutputs():
for in_ in a.getinputs():
if in_.Plug.isconnected(out.Plug):
return True
# on n'a trouve aucune connection
return False
Sélectionnez deux nœuds puis lancez :
# recupere les deux premiers noeuds de la selection
a, b = guerilla.Document().selection()[0:2]
print are_connected(a, b) # devrait afficher True
Itérer à travers tous les nœuds entrant dans un nœud donné
def input_connected_nodes(node):
for in_ in node.getinputs():
connected_plug = in_.getconnected()
if not connected_plug:
continue
connected_node = connected_plug.parent
yield connected_node
# on fait ca recursivement
input_connected_nodes(connected_node)
Sélectionnez un nœud connecté à d’autres :
Puis exécutez :
node = guerilla.Document().selection()[0]
print [n.name for n in input_connected_nodes(node)]
# ['Trace', 'Surface', 'All']
Vous pouvez modifier cette fonction pour qu’elle fonctionne avec les nœuds sortants en remplaçant la méthode getinputs()
par getoutputs()
.
Itérer à travers les nœuds à la gauche d’un nœud donné
import guerilla
def left_nodes(node):
# recupere la position du noeud source
src_x, src_y = node.NodePos.get()
# itere a travers tous les noeuds freres
for n in node.parent.children():
# recupere la position
x, _ = n.NodePos.get()
# renvoie le noeud uniquement s'il se trouve a gauche du noeud source
if x < src_x:
yield n
Sélectionner un nœud dans un RenderGraph
puis exécutez :
node = guerilla.Document().selection()[0]
print [n.name for n in left_nodes(node)]
Ceci affichera le nom de tous les nœuds positionné à gauche du nœud sélectionné.
Connecter un nœud entre deux autres
Cette fonction connectera le nœud b
entre a
et c
de sorte que a
sera connecté à b
, lui-même connecté à c
.
Elle part du principe que :
a
n’a qu’une sortie.b
n’a qu’une entrée et qu’une sortie.c
n’a qu’une entrée.
Cela permet de connecter dynamiquement des Binop
.
import guerilla
def put_between(a, b, c):
# on part du principe que les noeuds n'ont qu'une entree et sortie
a_out = a.getoutputs()[0].Plug
b_in = b.getinputs()[0].Plug
b_out = b.getoutputs()[0].Plug
c_in = c.getinputs()[0].Plug
# deconnecte a et c s'ils sont connecte
if c_in.isconnected(a_out):
c_in.disconnect(a_out)
b_in.connect(a_out)
c_in.connect(b_out)
Sélectionnez trois nœuds dans l’ordre de connexion voulu :
a,b,c = guerilla.Document().selection()[0:3]
# il vous faut un contexte de modification, car vous allez modifier le graph
with guerilla.Modifier() as mod:
put_between(a, b, c)
Essayez d’améliorer le code pour bouger le nœud c
de sorte qu’il se place entre a
et b
. ;)
Frame (ressemblant au backdrop Nuke)
Créer une frame
Ceci cré un nœud GraphFrame
:
import guerilla
# recupere le noeud RenderGraph
rg = guerilla.pynode("RenderGraph")
# il vous faut un contexte de modification, car vous modifiez le gproject
with guerilla.Modifier() as mod:
frame = mod.createnode("NewFrame", type="GraphFrame", parent=rg)
# vous pouvez personnaliser la frame
frame.Notes.set("TOTO!")
frame.Position.set((-200, -50))
frame.Size.set((200, 150))
frame.Color.set((1.0, 0.5, 0.6))
frame.Opacity.set(0.2)
Itérer à travers les nœuds positionnés dans une GraphFrame
donné
def nodes_in_frame(frame):
# on recupere la position et la taille
p_x, p_y = frame.Position.get()
s_x, s_y = frame.Size.get()
# on calcule les bords de la boite
x_min = p_x
x_max = p_x+s_x
y_min = p_y
y_max = p_y+s_y
# notez qu'on itere uniquement a travers les noeuds heritant du type
# 'RenderGraphNode', car ils disposent de l'attribut'NodePos'.
for node in frame.parent.children(type='RenderGraphNode'):
n_x, n_y = node.NodePos.get()
# test si la position du noeud est dans la frame
if all((x_min < n_x < x_max,
y_min < n_y < y_max)):
yield node
Si nous utilisons la variable frame
créé dans l’exemple précédent :
print [n.name for n in nodes_in_frame(frame)]
# ['Layer', 'Output']
Savoir si un nœud est à l’intérieur d’une frame
Il s’agit d’une variation de la fonction précédente. Celle-ci renvoi True
si le nœud donné est à l’intérieur de la frame donnée.
def is_node_in_frame(node, frame):
# on recupere la position et la taille
p_x, p_y = frame.Position.get()
s_x, s_y = frame.Size.get()
# on calcule les bords de la boite
x_min = p_x
x_max = p_x+s_x
y_min = p_y
y_max = p_y+s_y
n_x, n_y = node.NodePos.get()
# test si la position du noeud est dans la frame
return all((x_min < n_x < x_max,
y_min < n_y < y_max))
Avec le GraphFrame
cré précédemment :
node = guerilla.pynode("RenderGraph|Layer")
print node_in_frame(node, frame)
# True
Créer une macro à l’intérieur d’un RenderGraph
Voici un bout de code permettant de créer une macro en script de la même façon dont Guerilla le fait quand on s’y prend manuellement :
import guerilla
rg = guerilla.pynode("RenderGraph")
with guerilla.Modifier() as mod:
macro = mod.createnode("NewMacro", type="RenderGraphMacro", parent=rg)
out = mod.createnode("Output", type="RenderGraphMacroOutput", parent=macro)
in_ = mod.createnode("Input1", type="RenderGraphInput", parent=out)
out2 = mod.createnode("Output1", type="RenderGraphOutput", parent=macro)
out2.Plug.adddependency(in_.Plug)
in_.PlugName.connect(out2.PlugName)
Créer un sub shader
Ce bout de code créer un subshader sur le paramètre Dirt du matériau Surface par défaut :
import guerilla
# materiau "Surface" par defaut
surf_shader = guerilla.pynode("RenderGraph|Surface")
with guerilla.Modifier() as gmod:
# cree un sub shader vide sur l'attribut "Dirt"
sub_shader = gmod.createnode("Dirt", type="ShaderNodeMacro", parent=surf_shader)
# cree un noeud de sorti dans le sub shader
main_out = gmod.createnode("Output", type="ShaderNodeMacroOutput", parent=sub_shader)
main_out.NodePos.set((-90,-17.5))
# cree l'entre du noeud de sorti
in_node = gmod.createnode("Input1", type="ShaderNodeIn", parent=main_out)
in_node.Desc.set(guerilla.types('color'))
in_node.Value.set((0.5, 0.0, 0.5))
# cree la sortie cache du noeud de sorti principal
out_plug = gmod.createnode("Output1", type="ShaderNodeOut",parent=sub_shader)
out_plug.PlugName.set("Output")
gmod.adddependency(out_plug.Plug, in_node.Plug)
gmod.connect(in_node.PlugName, out_plug.PlugName)
Savoir si un nœud a un attribut d’entrée ou de sorti avec le nom donné
L’argument de la méthode hasPlug()
doit être le nom de la plug, mais ce nom peut être différent du nom affiché dans la UI. Ce dernier étant stocké dans l’attribut PlugName
. Cette fonction permet de vérifier qu’une plug existe suivant la valeur de PlugName
.
def has_plug_name(node, name, inputs=True, outputs=True):
'''Return if the given node has plug with PlugName attribute with the given
name.
:param (guerilla.Node) node:
Guerilla node to check for inputs/outputs names
:param (str) name:
The plug name to look for
:param (bool) inputs:
If True, will check for input plug names
:param (bool) outputs:
If True, will check for output plug names
'''
if inputs:
for i in node.getinputs():
if i.PlugName.get() == name:
return True
if outputs:
for o in node.getoutputs():
if o.PlugName.get() == name:
return True
return False
Maintenant, vérifions que le nœud par défaut Trace
a un attribut d’entrée set
:
import guerilla
rg = guerilla.pynode("RenderGraph")
print rg.hasPlug("set")
# False
print has_plug_name(rg.Trace, name="set", inputs=True, outputs=False)
# True
Récupérer le nœud connecté à l’attribut donné d’un nœud donné
Cette fonction renvoie le nœud connecté à l’entrée ou la sortie du nœud donné ayant le nom donné. Il est important de comprendre que s’il y a plusieurs nœuds d’entrées ou de sorties avec le nom donné, seul le premier est donné (cela se produit rarement, mais il faut en être conscient) :
def connected_node(node, name, inputs=True, outputs=True):
if inputs:
for i in node.getinputs():
if i.PlugName.get() == name:
con_out = i.getconnected()
if con_out:
return con_out.parent
if outputs:
for o in node.getoutputs():
if o.PlugName.get() == name:
for con_in in o.getconnected():
return con_in.parent
Ici, nous récupérons le nœud connecté à l’attribut d’entrée set
du nœud Trace
:
import guerilla
rg = guerilla.pynode("RenderGraph")
node = connected_node(rg.Trace, name="set", inputs=True, outputs=False)
print node.name
# "Surface"
Comme dis précédemment, dans le cas des nœuds de sortis, cette fonction ne renvoie que le premier nœud de sorti trouvé. Vous pouvez modifier cette fonction de sorte qu’elle renvoie une liste au lieu de s’en tenir au premier.
Créer le nœud de RenderGraph par défaut
L’API Python de Guerilla ne fournis pas de fonction permettant la création du nœud de RenderGraph
par défaut, il faut donc l’écrire vous-même :
import guerilla
def create_render_graph():
doc = guerilla.Document()
# create render graph
rg = guerilla.Node.create('RenderGraph', type='RenderGraph', parent=doc)
# tag node
all_ = guerilla.Node.create('All', type='RenderGraphNodeTag', parent=rg)
all_.Tag.set('All')
all_.Lights.set(True)
all_out = all_.createoutput()
all_out.PlugName.set('Output')
# surface node
surf = guerilla.Node.create('Surface2', type='RenderGraphNodeShader', parent=rg)
surf.Mode.set('surface')
surf.Shader.set('Surface2')
surf_in = surf.createinput()
surf_out = surf.createoutput()
# Trace node
trace = guerilla.Node.create('Trace', type='RenderGraphNodeSet', parent=rg)
trace.Membership.set('All,Diffuse,Reflection,Refraction')
trace_in = trace.createinput()
trace_in.PlugName.set('set')
trace_out = trace.createoutput()
# Lighting node
light = guerilla.Node.create('Lighting', type='RenderGraphNodeSet', parent=rg)
light.Membership.set('Lights,Shadows')
light_in = light.createinput()
light_in.PlugName.set('set')
light_out = light.createoutput()
# all -> surface
surf_in.Plug.connect(all_out.Plug)
# surface -> trace
trace_in.Plug.connect(surf_out.Plug)
# trace -> light
light_in.Plug.connect(trace_out.Plug)
# Layer node
lay = guerilla.Node.create('Layer', type='RenderGraphNodeRenderLayer',
parent=rg)
lay.Membership.set("layer:Layer")
lay_vis = lay.createinput()
lay_vis.PlugName.set('visible')
lay_matte = lay.createinput()
lay_matte.PlugName.set('matte')
lay_out = lay.createoutput()
# trace -> layer
lay_vis.Plug.connect(light_out.Plug)
# Output node
out = guerilla.Node.create('Output', type='RenderGraphNodeOutput',
parent=rg)
out_in = out.createinput()
out_in.PlugName.set('Output')
# layer -> out
out_in.Plug.connect(lay_out.Plug)
return rg
Savoir si un RenderGraphNode est connecté à la sortie d’un RenderGraph
La fonction suivante renvoie True
si le RenderGraphNode
donné est connecté à la sortie de son RenderGraph
.
def is_connected_to_rendergraph_output(node):
"""Returns True if the given RenderGraphNode is connected
to an output of the render graph."""
if getclassname(node) == "RenderGraphNodeOutput":
# It's connected
return True
elif getclassname(node) == "RenderGraphMacroOutput":
# Give the rendergraph macro itself
outplugs = node.getparent().getoutputs()
else:
# We will test all the output plugs of the node
outplugs = node.getoutputs()
for outplug in outplugs:
for c in outplug.Plug.connections(False, True):
# We recursively check if the connected nodes flow to the output
return is_connected_to_rendergraph_output(c.getnode().getparent())
# We found no connection, let's return
return False
Color range en SL
Guerilla provide a floatRange()
function but nothing to range color. Here is one:
Guerilla fourni une fonction floatRange()
mais rien pour un range de couleur (aucune colorRange()
). En voici une :
color color_range(color in; float inmin, inmax, outmin, outmax, clampIt)
{
color r = (in-inmin)/(inmax-inmin);
return outmin + (outmax-outmin)*(clampIt != 0 ? clamp (r, (0, 0, 0), (1, 1, 1)) : r);
}
World space AABB
Il n’y a aucune façon « officielle » de récupérer la WAABB, mais vous pouvez utiliser la fonction lua interne :
def world_aabb(node):
"""
Returns:
guerilla.aabb
"""
lua_aabb = lua.globals().getselectionworldaabb(guerilla.toLua(node))
return guerilla.fromLua(lua_aabb)
Scene
Itérer à travers chaque nœud et ses instances
Par défaut, Node.children()
n’itère pas à travers les instances. Si A
a un enfant B
et C
a un enfant B
qui est une instance pointant sur A|B
, Node.children()
n’itérera pas à travers C|B
et C
sera considéré comme n’ayant pas d’enfants :
La couleur bleutée de B
indique qu’il s’agit d’une instance. Itérer avec Node.children()
donnera :
A
A|B
C
L’itérateur suivant utilise l’attribut Node.Instances
pour trouver les instances et y itérer :
def walk(node, type_):
for child in node.children(type_):
yield child
for sub_child in walk(child, type_):
yield sub_child
for inst_plug in node.Instances.dependencies(source=False, destination=True):
inst = inst_plug.parent
yield inst
for child_inst in walk(inst, type_):
yield child_inst
Avec un tel itérateur, notre précédente hiérarchie sera traversée de cette façon :
A
A|B
C
C|B
Gardez à l’esprit qu’un tel itérateur peut être très lent si votre scène a énormément d’instances, car il itérera de nombreuses fois à travers les mêmes nœuds.
Récupérer les informations de primitive (face count, vertex count, etc.)
Guerilla dispose d’une fonction non exposé permettant de récupérer des informations de shape :
def get_shape_info(node):
table = lua.execute('return(_"{}":getshapeinfo())'.format(node.path))
return {k: table[k] for k in (v for v in table # lua table to python dict.
Cette fonction renvoie un dictionnaire avec quelques informations utiles :
>>> get_shape_info(node)
{'VarsMemory': 20352L,
'CurveCount': 0L,
'VertexCount': 846L,
'FaceCount': 1688L}
N’étant pas exposées officiellement, gardez à l’esprit que ces informations peuvent être supprimées dans les prochaines versions.
Dernière mise à jour : mar. 02 mars 2021