【笔记】【Maya工具】Python For Maya(OpenMaya)(一)API基础与插件

Python Programming in maya

1
2
3
4
5
6
7
8
9
10
import maya.cmds as cmd
cmds.select(all=True)
cmds.delete()
for step in range(40):
cmds.polyCube(w=10,d=3,h=0.5)
cmds.move(3,step*0.5,0)
cmds.xform(ws = True,rotatePivot=(0,0,0))
cmds.rotate(0,step*10,0)


1. DAG——描述Maya中的功能节点:

DAG(Directed Acyclic Graph有向无环图)

  • Dag Node:Transform
  • Dag Node:Shape

DG:Dependency Graph依赖图

DAG path,某个对象的DAG path描述从根节点到物体的完整查找路径

MObject(Model Object)

  • maya.OpenMaya.MObject

MSelectionList(选择的模型对象列表)

  • maya.OpenMaya.MSelectionList
    • 创建一个选择列表
    • 在选择列表中添加、移除对象
    • 可遍历
    • etc
1
2
3
4
5
6
7
8
9
10
11
import maya.OpenMaya as OpenMaya
mSelectionList = OpenMaya.MSelectionList()
mSelectionList.add("<something_Name>")

mDagPath = OpenMaya.MDagPath()
mSelectionList.getDagPath(0,mDagPath)#返回0位置的path
print(mDagPath.fullPathName())

mObj = OpenMaya.MObject()
mSelectionList.getDependNode(0,mObj)
print(mObj.apiTypeStr())#kTransform

2. Traversing Hyper Graph in Maya Scene

MFn = Model Function Set

MFnMesh表示接收Mesh类型的函数集

  • create
  • modify
  • retrave
  • add Edge\face,subdivision

MFnDependencyNode:一组函数需要Dependency Node Type的数据,提供函数

  • create(dependency graph)
  • modify(dependency graph)
  • retrave(dependency graph)

可以通过MObj和MDagpath来提供到函数集的接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import maya.OpenMaya as OpenMaya
# Create a Selection List
mSel = OpenMaya.MSelectionList()
mSel.add("pPlane1")
# Create MObject
mObj = OpenMaya.MObject()
mDagPath = OpenMaya.MDagPath()
# Request Dependency Node and DagPath of the Obj
mSel.getDependNode(0,mObj)
mSel.getDagPath(0,mDagPath)
# Create Mesh function set
mFnMesh = OpenMaya.MFnMesh(mDagPath)#可以用mObj和mDagpath两种
print(mFnMesh.fullPathName())#|pPlane1|pPlaneShape1
#说明mFnMesh是在ShapeNode下运作的

# Dependency function set
mFnDependNode = OpenMaya.MFnDependencyNode(mObj)#只能用mObj
print(mFnDependNode.name())

MPlug提供操作功能的类(操作Plug Type)

Plug:节点属性的接口

  • create
  • modify
  • access(通过plug访问属性)
  1. Network Plug: Dependency Node Plug

  2. Non-Network Plug: User Defined Plug

MPlugArray

image-20220627211430302
1
2
3
4
5
6
7
# Get all the connections of a Shape node
mPlugArray = OpenMaya.MPlugArray()
mFnMesh.getConnections(mPlugArray)

print(mPlugArray.length())#2
print(mPlugArray[0].name())#pPlaneShape1.instObjGroups[0]
print(mPlugArray[1].name())#pPlaneShape1.inMesh
image-20220627212637049 image-20220627212705325

image-20220627213320170

看看这个就知道是在干嘛了,我们的最终目的是操纵polyPlane1进行细分

通过pPlaneShape1的plug接口,就可以完成

1
2
3
4
5
6
mPlugArray2 = OpenMaya.MPlugArray()
mPlugArray[1].connectedTo(mPlugArray2,True,False)#后面这两个bool值不太明白

print(mPlugArray2.length())#1
#len()不能用在这个对象上
print(mPlugArray2[0].name())#polyPlane1.output

image-20220627214040508

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import maya.OpenMaya as OpenMaya
# Create a Selection List
mSel = OpenMaya.MSelectionList()
mSel.add("pPlane1")
# Create MObject
mObj = OpenMaya.MObject()
mDagPath = OpenMaya.MDagPath()
# Request Dependency Node and DagPath of the Obj
mSel.getDependNode(0,mObj)
mSel.getDagPath(0,mDagPath)
# Create Mesh function set
mFnMesh = OpenMaya.MFnMesh(mDagPath)#可以用mObj和mDagpath两种
print(mFnMesh.fullPathName())#|pPlane1|pPlaneShape1
#说明mFnMesh是在ShapeNode下运作的

# Dependency function set
mFnDependNode = OpenMaya.MFnDependencyNode(mObj)#只能用mObj
print(mFnDependNode.name())

# Get all the connections of a Shape node
mPlugArray = OpenMaya.MPlugArray()
mFnMesh.getConnections(mPlugArray)

print(mPlugArray.length())
print(mPlugArray[0].name())
print(mPlugArray[1].name())

mPlugArray2 = OpenMaya.MPlugArray()
mPlugArray[1].connectedTo(mPlugArray2,True,False)#后面这两个bool值不太明白

print(mPlugArray2.length())#len()不能用在这个对象上
print(mPlugArray2[0].name())

mObj2 = mPlugArray2[0].node()
mFnDependNode2 = OpenMaya.MFnDependencyNode(mObj2)
print(mFnDependNode2.name())

mPlug_width = mFnDependNode2.findPlug("width")
mPlug_height = mFnDependNode2.findPlug("height")
print(mPlug_width.asInt(),mPlug_height.asInt())

mPlug_subWidth = mFnDependNode2.findPlug("subdivisionsWidth")
mPlug_subHeight = mFnDependNode2.findPlug("subdivisionsHeight")

mPlug_subWidth.setInt(20)
mPlug_subHeight.setInt(20)


3. Command Communication Flow

image-20220627220119395
  • Command Plugin
    • 函数
    • 初始化/注册Initialization,Registration
    • Uninitialization/De-registration

把东西注入到Maya core,就是注册

  • 命令插件编写基本流程
    • 定义一个类
    • 创建一个实例
    • 用一个指针指向这个实例
    • Maya获取这个指针

4. Writing command plugin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import maya.OpenMaya as OpenMaya
import maya.OpenMayaMPx as OpenMayaMPx
import sys

commandName = "pluginCommand"

class pluginCommand(OpenMayaMPx.MPxCommand):
def __init__(self):
OpenMayaMPx.MPxCommand.__init__(self)

def doIt(self,argList):
print("doIt")
#创建实例并添加指针
def cmdCreator():
return OpenMayaMPx.asMPxPtr(pluginCommand())

#初始化
def initializePlugin(mobject):
mplugin = OpenMayaMPx.MFnPlugin(mobject)
try:
mplugin.registerCommand(commandName,cmdCreator)#mayaCore handle
except:
sys.stderr.write("Failed to register command:" + commandName)

def uninitializePlugin(mobject):
mplugin = OpenMayaMPx.MFnPlugin(mobject)
try:
mplugin.deregisterCommand(commandName)
except:
sys.stderr.write("Failed to deregister command" + commandName)

保存脚本后,在maya中加载插件

Mel,python,mayaUI三种方法

window-setting preferences-pluginManager

image-20220627222747095

image-20220627222850467

1
cmds.loadPlugin("./pluginCommand.py")

5. Maya API Iterators

Iterator是一个对象,对数据结构进行遍历

  • MItDag
    • Dag上的iterator
    • 深度优先
    • 广度优先
image-20220627230758404
  • MItDependencyGraph
image-20220627230836150
  • MItMeshEdge
image-20220627230927076
  • MItMeshVertex
image-20220627231006974
  • MItMeshPolygon(face)
image-20220627231033010
  • MItSurfaceCV(CVs of Nurbs)
image-20220627231104357
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import maya.OpenMaya as OpenMaya

dagIterator = OpenMaya.MItDag(OpenMaya.MItDag.kDepthFirst, OpenMaya.MFn.kInvalid)#arg1=树搜索算法,arg2 = Filtering
dagNodeFn = OpenMaya.MFnDagNode()

while (not dagIterator.isDone()):
currentObj = dagIterator.currentItem()#return dagNode
depth = dagIterator.depth()
dagNodeFn.setObject(currentObj)

name = dagNodeFn.name()
type = currentObj.apiTypeStr()
path = dagNodeFn.fullPathName()

printOut = ""
for i in range(0,depth):
printOut += "----->"
printOut += name + " : " + type
print(printOut)
dagIterator.next()

===========
World : kWorld
----->persp : kTransform
----->----->perspShape : kCamera
----->top : kTransform
----->----->topShape : kCamera
----->front : kTransform
----->----->frontShape : kCamera
----->side : kTransform
----->----->sideShape : kCamera
----->pCube1 : kTransform
----->----->pCubeShape1 : kMesh
----->----->pCylinder1 : kTransform
----->----->----->pCylinderShape1 : kMesh
----->----->----->pCone1 : kTransform
----->----->----->----->pConeShape1 : kMesh
----->----->----->----->pPlane1 : kTransform
----->----->----->----->----->pPlaneShape1 : kMesh
----->----->----->----->----->pPipe1 : kTransform
----->----->----->----->----->----->pPipeShape1 : kMesh

放到插件的doIt方法里面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import maya.OpenMaya as OpenMaya
import maya.OpenMayaMPx as OpenMayaMPx
import sys

commandName = "printHierarchy"

class pluginCommand(OpenMayaMPx.MPxCommand):
def __init__(self):
OpenMayaMPx.MPxCommand.__init__(self)

def doIt(self,argList):
print("Scene Heirarchy")
dagIterator = OpenMaya.MItDag(OpenMaya.MItDag.kDepthFirst, OpenMaya.MFn.kInvalid)#arg1=树搜索算法,arg2 = Filtering
dagNodeFn = OpenMaya.MFnDagNode()

while (not dagIterator.isDone()):
currentObj = dagIterator.currentItem()#return dagNode
depth = dagIterator.depth()
dagNodeFn.setObject(currentObj)

name = dagNodeFn.name()
type = currentObj.apiTypeStr()
path = dagNodeFn.fullPathName()

printOut = ""
for i in range(0,depth):
printOut += "----->"
printOut += name + " : " + type
print(printOut)
dagIterator.next()
#创建实例并添加指针
def cmdCreator():
return OpenMayaMPx.asMPxPtr(pluginCommand())

#初始化
def initializePlugin(mobject):
mplugin = OpenMayaMPx.MFnPlugin(mobject)
try:
mplugin.registerCommand(commandName,cmdCreator)
except:
sys.stderr.write("Failed to register command:" + commandName)

def uninitializePlugin(mobject):
mplugin = OpenMayaMPx.MFnPlugin(mobject)
try:
mplugin.deregisterCommand(commandName)
except:
sys.stderr.write("Failed to deregister command" + commandName)

保存脚本然后

1
2
3
import maya.cmds as cmds
cmds.loadPlugin("c:\\Users\\XZYW\\Desktop\\printHierarchy.py")
cmds.printHierarchy()

插件就可用了,这就是编写插件的方式。

6. Command with Arguments

1
cmds.polySphere("mySphere",q = True,sx = True)

image-20220628205624179

  • MSyntax
    • flags
    • Args
    • Object
  • MArgDatabase
    • 是MArgParser的派生类
    • Parsing
    • Storing
    • Retreving
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import maya.OpenMaya as OpenMaya
import maya.OpenMayaMPx as OpenMayaMPx
import sys
import maya.OpenMayaFX as OpenMayaFX
#使用粒子系统

commandName = "vertexParticle"
kHelpFlag = "-h"
kHelpLongFlag = "-help"
kSparseFlag = "-s"
kSparseLongFlag = "-sparse"
helpMessage = "This command is used to attach a particle on each Vertex of a poly mesh"

class pluginCommand(OpenMayaMPx.MPxCommand):
sparse = None
def __init__(self):
OpenMayaMPx.MPxCommand.__init__(self)


def argumentParser(self,argList):
syntax = self.syntax()#MSyntax Object
parsedArguments = OpenMaya.MArgDatabase(syntax,argList)
if parsedArguments.isFlagSet(kSparseFlag):
self.sparse = parsedArguments.flagArgumentDouble(kSparseFlag,0)
return
if parsedArguments.isFlagSet(kSparseLongFlag):
self.sparse = parsedArguments.flagArgumentDouble(kSparseLongFlag,0)
return
if parsedArguments.isFlagSet(kHelpFlag):
self.setResult(helpMessage)
return
if parsedArguments.isFlagSet(kHelpLongFlag):
self.setResult(helpMessage)
return# OpenMaya.MStatus.kSuccess这个方法已经被取消了,其实不用返回任何东西
def isUndoable(self):
return True
def undoIt(self):
print("Undo")
mFnDagNode = OpenMaya.MFnDagNode(self.mObj_particle)

mDagMod = OpenMaya.MDagModifier()
mDagMod.deleteNode(mFnDagNode.parent(0))#粒子系统创建是带有Transform的

mDagMod.doIt()
return

def redoIt(self):
mSel = OpenMaya.MSelectionList()
mDagPath = OpenMaya.MDagPath()
mFnMesh = OpenMaya.MFnMesh()
OpenMaya.MGlobal.getActiveSelectionList(mSel)#处理所有主动选择的对象,并添加到提供的选择列表中
if mSel.length() >=1:
try:
mSel.getDagPath(0,mDagPath)
mFnMesh.setObject(mDagPath)
except:#如果选中的不是polymesh
print("Select a poly mesh")

return
else:#如果没选东西
print("Select a poly mesh")
return
mPointArray = OpenMaya.MPointArray()
mFnMesh.getPoints(mPointArray, OpenMaya.MSpace.kWorld)#提供所有顶点世界坐标

# create a particle system
mFnParticle = OpenMayaFX.MFnParticleSystem()
self.mObj_particle = mFnParticle.create()

# Fix Maya Bug
mFnParticle = OpenMayaFX.MFnParticleSystem(self.mObj_particle)

counter = 0
# attach particle to vertex
for i in range(mPointArray.length()):
if i%self.sparse == 0:
mFnParticle.emit(mPointArray[i])
counter += 1
print("Total Points:" + str(counter))
mFnParticle.saveInitialState()#如果应用动力学,确保粒子在原来位置上去应用
return

def doIt(self,argList):
print("doIt")
self.argumentParser(argList)
if self.sparse != None:
self.redoIt()
return

#创建实例并添加指针
def cmdCreator():
return OpenMayaMPx.asMPxPtr(pluginCommand())
#创建命令语法
def syntaxCreator():
# create MSyntax object
syntax = OpenMaya.MSyntax()
# collect/add the flags
syntax.addFlag(kHelpFlag,kHelpLongFlag)#short name,long name,data type
syntax.addFlag(kSparseFlag,kSparseLongFlag,OpenMaya.MSyntax.kDouble)
# return MSyntax
return syntax


#初始化
def initializePlugin(mobject):
mplugin = OpenMayaMPx.MFnPlugin(mobject)
try:
mplugin.registerCommand(commandName,cmdCreator,syntaxCreator)#同时注册语法
except:
sys.stderr.write("Failed to register command:" + commandName)

def uninitializePlugin(mobject):
mplugin = OpenMayaMPx.MFnPlugin(mobject)
try:
mplugin.deregisterCommand(commandName)
except:
sys.stderr.write("Failed to deregister command" + commandName)


1
2
3
4
range 是生成一个列表
xrange用法与range完全相同,不同的是生成的不是一个list对象,而是一个生成器
在生成很大的数字序列时候,用xrange会比range性能优很多,因为不需要一上来就开辟一块很大的内存空间
xrange和range都在循环的时候使用。
  • Command Execution
  • Undo
  • Redo

每执行一个Command Execution,都会把对应的命令添加到Undo队列

如果Command Execution使用Undo命令,就会把Undo队列中最后面的移到Redo队列上

如果Command Execution使用Redo命令,就会把Redo队列中最后面的移到Undo队列上

image-20220628211342017

image-20220628213019559

好了,写完了这个脚本,看完这集教程,终于发现,它是过时的版本,作者用的还是maya2013,非常恐怖