本文介绍的是Maya中blendShape的底层原理,并总结了一些通过Python代码编辑blendShape的方法。代码库为cmds和openMaya。注意,阅读本文需要一定的编程基础。
一、blendShape节点属性介绍
在“窗口>>节点编辑器”中打开节点编辑器,按下“Tab”并输入“blendshape”创建一个完全无数据的空blendShape;右键按住并移动到“显示所有属性”一栏再松开,可以看到节点的所有属性;可以点击名称处修改它的名字。
在节点编辑器中创建blendShape
可以看到BS节点中的属性有很多,但重点只有四个,我们使用上一节中“在形变编辑器中创建blendShape“的方法创建好BS后,右键点击“在节点编辑器中查看”选项,可以看到系统为您连好的BS节点。
原始几何体
原始几何体用来获取该BS的初始形状,与“pSphereShape1Orig”链接代表该BS从网格“pSphereShape1”的Orig节点中获取初始形状。(Orig节点是网格的原始节点,记录网格的最原始的数据,可能不与显示在窗口中的形状相同。)
输出几何体
BS将修改后的形状通过“输出几何体”属性输出,与“pSphereShape1”链接代表形变后的形状会赋予给“pSphereShape1”,使该网格显示在窗口中的形状发生改变。(不带后缀的节点是网格的显示节点,用来记录网格显示在窗口中的形状数据,即我们看到的形状。)
权重
一个BS中可能包含多个目标形状,这些形状的权重值和名字就记录在权重当中,由于大部分时候我们不会改变权重的大小,所以我通常使用该属性方便的遍历BS的目标形状名称、获取BS的目标形状数量。
输入目标
输入目标中包含的信息最多我们重点关注:输入目标[x],输入点目标,输入组件目标。每个输入目标[x]都代表一个目标网格;“输入点目标”中记录的是该目标网格会影响的点的ID;“输入组件目标”中记录的是该目标网格会影响的点的位移量。因此,我们可以通过改变这些数据实现快速批量控制BS。
图四:BS输入目标属性表
二、使用OpenMaya编辑BS
我尝试了cmds、mel、pymel、OpenMaya等库与语言后,选择了OpenMaya来实现BS的编辑,因为cmds、mel、pymel都是调用OpenMaya来实现BS的编辑,速度慢不说,可实现的功能也不多。因此,使用OpenMaya改写blendShape节点可以说是最优解,且AutoDesk官方给出了API,学习起来也很方便。
这里我将介绍,通过OpenMaya创建BS的目标网格的方法、通过OpenMaya修改BS的目标网格的方法。
首先要导入OpenMaya库,获取BS节点,在创建目标形状,最后给入形变数据。
import maya.OpenMaya as OM
# BS的名称
BSName = "blendShape1"
# 获取OpenMaya中的BS节点
# OpenMaya中的BS节点为MFnDependencyNode
BSNodeObj = OM.MObject()
sel = OM.MSelectionList()
sel.add(BSName, 0)
sel.getDependNode(0, BSNodeObj)
dgFn = OM.MFnDependencyNode(BSNodeObj)
# 使用plug获取节点中的属性
# findPlug('inputTarget')为图四中的输入目标接口
# elementByLogicalIndex是Mplug的函数,用来返回plug的逻辑节点的元素,如果找不到,就创建它。此处的elementByLogicalIndex(0)代表输入目标[0]
# child(0)返回的值为“输入目标[0]-输入目标组”
plug = dgFn.findPlug('inputTarget').elementByLogicalIndex(0).child(0)
# ------------------------------------------------------------------
# 下面的函数用来创建一个新的目标形状,值得注意的是其中有个值为6000的逻辑地址,
# 它根据该目标网格的权重值得到,6000=w*1000+5000
tarId = 0
tarName = "testTarName"
# create target
custPlug.elementByLogicalIndex(tarId).asMObject()
custPlug.evaluateNumElements()
# create LogicalIndex6000 on the target
input_target_item_mPlug = custPlug.elementByLogicalIndex(tarId).child(0)
input_target_item_mPlug.elementByLogicalIndex(6000)
input_target_item_mPlug.evaluateNumElements()
# create weight of the target
weight_mPlug = custDgFn.findPlug("weight", False)
weight_mPlug.elementByLogicalIndex(tarId).asFloat()
weight_mPlug.evaluateNumElements()
# assign the name to the new target
cmds.aliasAttr(tarName, '{}.w[{}]'.format(BSName, tarId))
# ------------------------------------------------------------------
# 下面的函数用来给入BS形变数据
# points记录顶点delta数据,可以完全自定义,形如:[ [0,0,0] , [1,1,1] , [0,0,1] ]
# indices记录delta数据对应的顶点ID, 长度要与points一致,形如:[ 0,1,3 ]
points = [ [1,0,0] , [1,1,1] , [0,0,1] ]
indices = [ 0,1,3 ]
# 将列表转换成MObject类型的数据
point_list = OM.MPointArray()
index_list = OM.MIntArray()
# get point_list
for pointDelta in points:
point = OM.MPoint(pointDelta[0], pointDelta[1], pointDelta[2], 1.0)
point_list.append(point)
# writes the delta data in the inputPointsTarget plug
dg_pointArray_fn = OM.MFnPointArrayData()
dg_pointArray_data = dg_pointArray_fn.create(point_list)
plug.elementByLogicalIndex(tarId).child(0).elementByLogicalIndex(6000).child(3).setMObject(dg_pointArray_data)
# get index_list
for index in indices:
index_list.append(index)
# initializes function sets, needed to create the MObjects and set their data
dg_component_fn = OM.MFnComponentListData()
dg_component_data = dg_component_fn.create()
singleComponent_fn = OM.MFnSingleIndexedComponent()
singleComponent_data = singleComponent_fn.create(OM.MFn.kMeshVertComponent)
singleComponent_fn.addElements(index_list)
dg_component_fn.add(singleComponent_data)
plug.elementByLogicalIndex(tarId).child(0).elementByLogicalIndex(6000).child(4).setMObject(dg_component_data)
# ------------------------------------------------------------------
# 至此,一个完全自定义的目标形状就创建完了,提醒一下BS数据,可以直接从现有BS中获取,
# 且不必转换来转换去的,还麻烦
参考链接:
1、OpenMaya API链接:
https://help.autodesk.com/view/MAYAUL/2023/CHS/?guid=MAYA_API_REF_py_ref_namespace_open_maya_html
2、外网参考
https://groups.google.com/g/python_inside_maya/c/_GiD2R89Dqg