Extrait de code Maya
Récupérer la position de chaque vertex d’un nœud de mesh
Il s’agit d’une méthode extremenent rapide :
import maya.cmds as mc
raw_pos = mc.xform('pCube1.vtx[*]', query = True, worldSpace = True, translation = True)
vtx_pos = zip(raw_pos[0::3], raw_pos[1::3], raw_pos[2::3])
Récupérer l’index du vertex le plus proche d’une position donnée
import maya.cmds as mc
def sqrt_dst(p1,p2):
"""return square distance between `p1` and `p2`
This assume `p1` and `p2` are tuple of three component each.
"""
return (p1[0]-p2[0])**2+(p1[1]-p2[1])**2+(p1[2]-p2[2])**2
def closest(org_pos, node):
"""Return vertex indice of given node wich is the closest of given world space `org_pos`"""
# get every world space position of given node vertices
raw_pos = mc.xform(node+'.vtx[*]', query = True, worldSpace = True, translation = True)
# convert raw list to list of three-component-tuple
pt_pos = zip(raw_pos[0::3], raw_pos[1::3], raw_pos[2::3])
pt_sqrt_dst = [sqrt_dst(org_pos, pt) for pt in pt_pos]
return pt_sqrt_dst.index(min(pt_sqrt_dst))
# get world position of the locator
loc_pos = mc.xform('locator1', query = True, worldSpace = True, translation = True)
print closest(org_pos = loc_pos, node = 'pCube1')
Itérer sur les top nodes de la scène
import maya.cmds as mc
# to avoid to get default cameras, we create a set with default nodes
defaultNodes = set(mc.ls(defaultNodes=True))
def top_nodes():
# iter over every top transform nodes
for node in mc.ls(assemblies = True):
# skip default nodes and nodes having parent
if node in defaultNodes:
continue
yield node
Créez un groupe dans une scène vide (ctrl+g) :
print list(top_nodes())
# [u'null1']
Iterate over nodes having non-ascii characters in their names
This snippet run though every node in the current scene and detect if there is non ascii characters in node names.
import maya.cmds as mc
def non_ascii_named_nodes():
# iterate over every node of the current scene
for node in mc.ls('*'):
# try to read node name in ascii...
try:
node.decode('ascii')
except UnicodeEncodeError:
# ...and return the node if its fail
yield node
Create a null node and rename it "pâté" then run the command:
print list(non_ascii_named_nodes())
# [u'p\xe2t\xe9']
Maya can print correct values like this:
for node in non_ascii_named_nodes():
print node
# pâté
Iterate over nodes having invalid characters in their names
import string
import maya.cmds as mc
# store every valid characters for later check
_valid_chars = set(string.ascii_letters + string.digits + '_')
def invalid_named_nodes():
return (n for n in mc.ls('*') # return each node
if any((c not in _valid_chars # having a invalid character
for c in n)))
And use it like this:
for invalid_node in invalid_named_nodes():
print invalid_node
# pâté
Convert non-ascii node names to valid ascii
Following previous snippet, this one will rename every node to ensure it has a ascii complient name.
import unicodedata
import maya.cmds as mc
has_ascii = True
while has_ascii:
for node in mc.ls('*'):
try:
node.decode('ascii')
except UnicodeEncodeError:
new_name = unicodedata.normalize('NFKD', node).encode('ascii', 'ignore')
print "Rename non-ASCII node: ", node, "->", new_name
mc.rename(node, new_name)
break
else: # no break
has_ascii = False
# Rename non-ASCII node: pâté -> pate
You have to be careful as new name can potentially clash with another node.
This snippet is inspired from here.
Add a new menu
import maya.mel as mel
# get main window menu
mainMayaWindow = mel.eval('$nothing = $gMainWindow')
# top menu
menu = mc.menu('Coucou!', parent = mainMayaWindow)
mc.menuItem(label = "Another Manager", command = "print 'another manager'", parent = menu)
# optionnal: add another entry in the menu with a function instead of a string
def do_something(arg):
print "Do something"
mc.menuItem(label = "Another Menu", command = do_something, parent = menu)
Retrive currently selected camera
It looks like there is two way to retrive currently focused camera.
import maya.cmds as mc
# method 1: get focused panel
focus_pan = mc.getPanel(withFocus=True)
# method 2: get what the playblast command consider as the active editor
focus_pan = mc.playblast(activeEditor=True)
# and finally get camera name from editor
cam = mc.modelPanel(focus_pan, query=True, camera=True)
If the focused panel is not a modelEditor
, method 1 will bring to a RuntimeError
when trying to retrieve camera name using modelPanel
. Method 2 seems to be more reliable.
Get materials connected to a list of shapes
You maybe know hyperShade()
command to select materials connected to selected objects:
>>> import maya.cmds as mc
>>> mc.hyperShade(mc.hyperShade(shaderNetworksSelectMaterialNodes=True)
>>> mc.ls(selection=True)
[u'lambert1']
But this command need UI.
Here is a version relying on connections:
>>> mshs = ['pSphereShape1']
>>> sgs = mc.listConnections(mshs, type='shadingEngine')
>>> sg_inputs = mc.listConnections(sgs, destination=False)
>>> mc.ls(sg_inputs, materials=True)
[u'lambert1']
Point to Camera space (PyMEL)
import pymel.core.datatypes as dt
import pymel.core as pm
cam = pm.PyNode("persp")
target = pm.PyNode("locator1")
# generate view projection matrix
cam_world_mtx = cam.getMatrix(worldSpace=True)
cam_world_inv_mtx = cam_world_mtx.inverse()
cam_proj_mtx = cam.projectionMatrix()
cam_vp_mtx = cam_world_inv_mtx * cam_proj_mtx
# get world space target position
target_pt = dt.Point(target.getTranslation(space='world'))
# compute projected target point
proj_pt = target_pt * cam_vp_mtx
# put in image space
proj_pt_x = (proj_pt.x / proj_pt.w) / 2.0 + 0.5
proj_pt_y = (proj_pt.y / proj_pt.w) / 2.0 + 0.5
print proj_pt_x, proj_pt_y
print proj_pt.w # distance from camera
Get used UDIMs on given mesh node
def udim_from_meshes(meshes):
udims = set()
for mesh in meshes:
try:
uvs = cmds.getAttr('{}.uvpt[:]'.format(mesh))
except ValueError:
continue # No UV on this mesh.
# Generate UDIM number and store in to the set.
udims |= {int(1000+(math.floor(u)+1)+(math.floor(v)*10))
for u, v in uvs}
return udims
Find optionVar
differences.
This code can be useful to find specific option var name:
# Gather all values.
d[v] = {k: cmds.optionVar(q=k)
for k in cmds.optionVar(list=True)}
# ...edit global preferences...
# List different ones.
for v in cmds.optionVar(list=True):
t = cmds.optionVar(q=v)
if t != d[v]:
print v, "old:", d[v], "new:", t
Maya Python Idioms
Those are idioms to do various things in Maya.
Get node name
If you want to get node name from a given node path, you can use split('|')[-1]
and be sure to get the node name.
>>> '|path|to|node'.split('|')[-1] # full node path
'node'
>>> 'path|to|node'.split('|')[-1] # relative node path
'node'
>>> 'node'.split('|')[-1] # even node name works
'node'
Get parent name from absolute node path
A safe way to get parent is to do:
>>> mc.listRelatives('|path|to|node', parent=True, fullPath=True)[0]
'|path|to'
The listRelatives()
command return a list
of parent node paths. A node can have multiple parent in case of instance (one shape, multiple parent transform
nodes).
But listRelatives()
is costly. If you have the garanty there is no instance in your scene and the input node path is absolute you can rely on string manipulation and use rsplit('|', 1)
to cut the string on the right and get parent node:
>>> '|path|to|node'.rsplit('|', 1)[0]
'|path|to'
Get absolute node path from relative node path
Some commands can't return absolute node path. The way to get absolute node path from relative node path is:
>>> mc.ls('to|node', long=True)[0]
'|path|to|node'
Progress message during Alembic exports
# compute the number of time the current time will change (here: 100)
_time_changed_max = 100.0
_time_changed_count = 0
def _progress_msg():
global _time_changed_count
_time_changed_count += 1
# compute percent value
percent = (_time_changed_count/_time_changed_max)*100.0
# round two decimal after the comma
print "{:.2f}%".format(percent)
job_num = mc.scriptJob(event=['timeChanged', _progress_msg])
# ...do your export here...
mc.scriptJob(kill=job_num, force=True)
Dernière mise à jour : mar. 22 novembre 2022