基本は
cmds.skinPercent( skincluster, vtx, transform=bone, query=True)
経過時間 = 00h:03m:16s
だけどgetAttrでとろうとするとこうなる
参考 http://www.marcuskrautwurst.com/2012/12/skinweights-savingloading-in-pymel.html
経過時間 = 00h:03m:38s ちょっと遅いんかい。
#get
skincluster="skinCluster1"
vert=8155
influence='joint1'
bones=pm.listConnections(str(skincluster) + ".matrix")
print("bones= "+str(bones))
boneInt=0
boneslen=len(bones)
for b in range(0,boneslen):
bonename=bones[b]
if influence in str(bonename):
print("found hit")
boneInt=b
print("boneInt= "+str(boneInt))
evalStr=str(skincluster)+'.weightList['+str(vert)+'].weights['+str(boneInt)+']'
resultVal=pm.getAttr(evalStr)
print("resultVal= "+str(resultVal))
setAttrするにもこう
#set
skincluster="skinCluster1"
vert=8155
influence='joint1'
weight=1
bones=pm.listConnections(str(skincluster) + ".matrix")
print("bones= "+str(bones))
boneInt=0
boneslen=len(bones)
for b in range(0,boneslen):
bonename=bones[b]
if influence in str(bonename):
print("found hit")
boneInt=b
print("boneInt= "+str(boneInt))
evalStr=str(skincluster)+'.weightList['+str(vert)+'].weights['+str(boneInt)+']'
#print("evalStr= "+str(evalStr))
#pm.setAttr('%s.weightList[%s].weights[%s]'%(skincluster,vert,skinData[each].influence),weights)
pm.setAttr(evalStr,weight)
om2でとるとこうなる。参考URL https://jamesbdunlop.github.io/om2/2018/03/29/IOSkins.html
import logging
import time, os
#import simplejson as json
import maya.api.OpenMaya as om2
import maya.cmds as cmds
#from OM2core.plugs.utils import getPlugValue, findPlugOnNode
def findPlugOnNode(node, plugName):
"""
:param node: MObjectHandle
:return: MPlug
"""
if not node.isValid():
raise Exception("Node is no longer valid!")
mFn = om2.MFnDependencyNode(node.object())
for x in xrange(mFn.attributeCount()):
attr = om2.MFnAttribute(mFn.attribute(x))
if attr.name == plugName:
attr = mFn.attribute(plugName)
plug = om2.MPlug(node.object(), attr)
return plug
raise Exception("PlugName {} is not valid!".format(plugName))
def getPlugValue(plug):
"""
:param plug: MPlug. The node plug.
:return: The value of the passed in node plug or None
"""
pAttribute = plug.attribute()
apiType = pAttribute.apiType()
# Float Groups - rotate, translate, scale; Com2pounds
if apiType in [om2.MFn.kAttribute3Double, om2.MFn.kAttribute3Float, om2.MFn.kCompoundAttribute]:
result = []
if plug.isCompound:
for c in xrange(plug.numChildren()):
result.append(getPlugValue(plug.child(c)))
return result
# Distance
elif apiType in [om2.MFn.kDoubleLinearAttribute, om2.MFn.kFloatLinearAttribute]:
return plug.asMDistance().asCentimeters()
# Angle
elif apiType in [om2.MFn.kDoubleAngleAttribute, om2.MFn.kFloatAngleAttribute]:
return plug.asMAngle().asDegrees()
# TYPED
elif apiType == om2.MFn.kTypedAttribute:
pType = om2.MFnTypedAttribute(pAttribute).attrType()
# Matrix
if pType == om2.MFnData.kMatrix:
return om2.MFnMatrixData(plug.asMObject()).matrix()
# String
elif pType == om2.MFnData.kString:
return plug.asString()
# MATRIX
elif apiType == om2.MFn.kMatrixAttribute:
return om2.MFnMatrixData(plug.asMObject()).matrix()
# NUMBERS
elif apiType == om2.MFn.kNumericAttribute:
pType = om2.MFnNumericAttribute(pAttribute).numericType()
if pType == om2.MFnNumericData.kBoolean:
return plug.asBool()
elif pType in [om2.MFnNumericData.kShort, om2.MFnNumericData.kInt, om2.MFnNumericData.kLong,
om2.MFnNumericData.kByte]:
return plug.asInt()
elif pType in [om2.MFnNumericData.kFloat, om2.MFnNumericData.kDouble, om2.MFnNumericData.kAddr]:
return plug.asDouble()
# Enum
elif apiType == om2.MFn.kEnumAttribute:
return plug.asInt()
return None
def _iterForSkinCluster(node):
"""
:param node: MObject for the source connection
:return: MObject
"""
if node.apiType() == om2.MFn.kSkinClusterFilter:
return om2.MObjectHandle(node)
iterDg = om2.MItDependencyGraph(node,
om2.MItDependencyGraph.kDownstream,
om2.MItDependencyGraph.kPlugLevel)
while not iterDg.isDone():
currentItem = iterDg.currentNode() # use currentItem for 2017 and below
if currentItem.hasFn(om2.MFn.kSkinClusterFilter):
return om2.MObjectHandle(currentItem)
iterDg.next()
def _findSkinCluster(mesh=None):
"""
Returns a skinCluster attached to the kMesh or kNurbsCurve
@:param mesh: MObjectHandle. Not the shape! Use the transform.
:return: MObject
"""
if not mesh.isValid():
logger.warning("Destination is no longer valid!")
return
dagPath = om2.MDagPath()
geo = dagPath.getAPathTo(mesh.object())
## Does it have a valid number of shapes?
if geo.numberOfShapesDirectlyBelow() != 0:
## Fetch the shape of the geo now.
shapeMobj = geo.extendToShape().node()
mFn_shape = om2.MFnDependencyNode(shapeMobj)
apiType = shapeMobj.apiType()
if apiType == om2.MFn.kMesh:
## Look at the inMesh attr for the source
inMesh_attr = mFn_shape.attribute('inMesh')
elif apiType == om2.MFn.kNurbsCurve:
inMesh_attr = mFn_shape.attribute('create')
else:
logger.warning("This type of om2.MFn node is not supported! int: {}".format(apiType))
return
inMesh_plug = om2.MPlug(shapeMobj, inMesh_attr)
getSource = inMesh_plug.source().node()
## Now use the iterForSkinCluster() function to find the skinCluster in the connected network.
skinClusterNode_MObjH = _iterForSkinCluster(getSource)
if skinClusterNode_MObjH is not None:
return skinClusterNode_MObjH
else:
return None
return None
def _findInfluences(skinClusterMobjH=None):
"""
Returns all the valid influences from the .matrix attribute on the skinCluster node.
@:param mesh: MObjectHandle for the skinCluster. Using the handles here may be playing it a little too safe. But meh.
:return: MObject
"""
if not skinClusterMobjH.isValid():
logger.warning("Skincluster is no longer valid! Did it get deleted?")
return
skClsMFnDep = om2.MFnDependencyNode(skinClusterMobjH.object())
mtxAttr = skClsMFnDep.attribute("matrix")
matrixPlug = om2.MPlug(skinClusterMobjH.object(), mtxAttr)
## Get a list of all the valid connected indices in the matrix array now.
indices = matrixPlug.getExistingArrayAttributeIndices()
influences = []
for idx in indices:
name = om2.MFnDependencyNode(matrixPlug.elementByLogicalIndex(idx).source().node()).absoluteName()
influences.append(str(om2.MNamespace.stripNamespaceFromName(name)))
return influences
def fileExists(filePath):
if filePath is not None and os.path.isfile(filePath):
logger.info("File exists!")
return True
return False
def writetoJSON(filePath, data):
with open(filePath, 'w') as outfile:
outfile.write(json.dumps(data))
return True
def readFromJSON(filePath):
with open(filePath) as infile:
data = json.load(infile)
return data
return None
def _fetchSkinWeights(geoList=None, skipZeroWeights=True):
"""
If you send in a list of geo, we'll use that. Else we assume we're working off selected.
:param geoList: list() of string names for geometry to fetch data for
:param skipZeroWeights: if you want to avoid saving all 0.0 weight data
:return:
"""
geo = om2.MSelectionList()
if geoList is not None:
for eachGeo in geoList:
geo.add(eachGeo)
else: #Assume selected
for eachGeo in cmds.ls(sl=True):
geo.add(eachGeo)
weightData = {}
for x in range(geo.length()):
geoMObjH = om2.MObjectHandle(geo.getDependNode(x))
geoName = om2.MFnDependencyNode(geoMObjH.object()).name()
skinClusterMObjH = _findSkinCluster(geoMObjH)
if skinClusterMObjH is None:
logger.warning("Skipping {} has no skinCluster!".format(geoName))
continue
skName = str(om2.MFnDependencyNode(skinClusterMObjH.object()).name())
influences = _findInfluences(skinClusterMObjH)
## Add the data to the dict
weightData[geoName] = {}
weightData[geoName][skName] = {}
weightData[geoName][skName]['influences'] = influences
weightData[geoName][skName]['maxInf'] = cmds.skinCluster(skName, q=True, maximumInfluences=True)
weightData[geoName][skName]['weights'] = {}
## Fetch the weights now
weightPlug = findPlugOnNode(skinClusterMObjH, 'weightList')
weightCount = weightPlug.getExistingArrayAttributeIndices()
for x in range(len(weightCount)):
# .weightList[x]
p = weightPlug.elementByLogicalIndex(weightCount[x])
# .weights
c = p.child(0)
## Now fetch the list of idx numbers that should relate to the inf [23, 66, 99]
w = c.getExistingArrayAttributeIndices()
## For each idx we're going to build a tuple (idx, infName, weightVal)
weightList = list()
for i in range(len(w)):
childPlug = c.elementByLogicalIndex(w[i])
weightValue = getPlugValue(childPlug)
if skipZeroWeights and weightValue == 0.0:
continue
idx = w[i]
weightList.append((idx, weightValue))
weightData[geoName][skName]['weights'][str(x)] = weightList
print("weightData["+str(geoName)+"]["+str(skName)+"][weights]["+str(x)+"]"+"= "+str(weightList))
return weightData
weightData=_fetchSkinWeights(None,False)
#print("weightData= "+str(weightData))
_Weights=weightData[dupMesh][skincluster]["weights"][str(vert)]
print("_Weights= "+str(_Weights))
boneInt=0
bones = pm.listConnections(str(skincluster) + ".matrix")
boneslen=len(bones)
for b in range(0,boneslen):
bonename=bones[b]
if influence in str(bonename):
#print("found hit")
boneInt=b
print("boneInt= "+str(boneInt))
weight=0
for _Weight in _Weights:
#_Weight=(0, 0.8388942003250122)
boneNum=_Weight[0]
if(boneNum==boneInt):
weight=_Weight[1]
print("weight= "+str(weight))
経過時間 = 00h:07m:10s om2なのに遅い。。。。
データ参照するためにfor分2個使うせいだな。
print消しても
経過時間 = 00h:04m:17s おそい。。。
もっと早く取りたい
skincluster.getPointsAffectedByInfluence
インフルエンス骨基準で頂点のウェイトを問い合わせ回数を減らすことで早くなる。
getData = skincluster.getPointsAffectedByInfluence(InfluenceJoint)
で取れるみたい。
詳しくはこちら。
https://www.marcuskrautwurst.com/2012/12/skinweights-savingloading-in-pymel.html
こんな感じで minidomに保存してそこからロードして使う。
import xml.dom.minidom as minidom
class SkinWeight_To_MiniDom():
def getMeshVertex(self,objName):
vtx = cmds.ls(objName+'.vtx[*]', fl=True)
return vtx
def _showProgressDialog(self):
self.WindowRef._showProgressDialog()#---------------------ProgressBar Starting--------------------------
def setProgress(self,count):
self.WindowRef.setProgress(count)
def fnFindSkinCluster(self,mesh):
skincluster = None
for each in pm.listHistory(mesh):
if type(each)==pm.nodetypes.SkinCluster:
skincluster = each
return skincluster
def fnSaveSkinning(self,mesh,path):
print("SkinWeight_To_MiniDom---------fnSaveSkinning("+mesh+" path: "+path+")")
# 次は、スキンウェイトの保存機能から始めましょう。
##skinClusterクラスには、influenceObjects()と呼ばれる関数があり、
##skinclusterにバインドされているすべてのジョイントを返します。
##次に、基本的に各ジョイントを反復処理し、各インフルエンスに対して関数getPointsAffectedByInfluence()を実行します。
##これにより、各頂点とその重みを含むリストが返されます。
##これらすべての値を適切なリストに保存し、minidomモジュールを利用してxmlファイルに保存できるようにします。
## データを収集します
skincluster = self.fnFindSkinCluster(mesh)
## スキンクラスターが存在するかどうかを確認します
if skincluster!=None:
print(u"スキンクラスターがありました!")
## XMLxml_documentを準備します
xml_doc = minidom.Document()
xml_header = xml_doc.createElement("influences")
xml_doc.appendChild(xml_header)
## ジョイントID/名前テーブルを書き出す
for each in skincluster.getInfluence():
getData = skincluster.getPointsAffectedByInfluence(each)
tmpJoint= xml_doc.createElement(each)
vertData = []
if len(getData[0])>0:
## すべての頂点IDを収集し、vertDataリストに保存します
for each in getData[0][0]:
vertData.append(each.currentItemIndex())
## 次に、vertDataリストを「vertices」属性値として保存します
tmpJoint.setAttribute("vertices",str(vertData))
tmpJoint.setAttribute("weights",str(getData[1]))
xml_header.appendChild(tmpJoint)
## Save XML
file = open(path, 'w')
file.write(xml_doc.toprettyxml())
pm.warning("Saved '%s.skinning' to '%s'"%(mesh,path))
else:
print(u"スキンクラスターがありません!")
pm.warning('No skincluster connected to %s'%mesh)
def fnLoadSkinning(self,mesh,path):
##次に、いくつかの変数を使用して関数を設定し、メッシュがすでにスキンされている場合は、そのスキンクラスターを削除するようにします。
##後で新しいジョイントを作成し、スキンウェイトファイルに基づいてすべてのジョイントを自動的に追加します。
## 変数
jointData = []
skinData = []
### メッシュがすでにスキンされている場合は、メッシュをクリーンアップします
skincluster = fnFindSkinCluster(mesh)
if skincluster!=None:
pm.runtime.DetachSkin(skincluster)
## 次に、xmlファイルを解析して、すべてのデータを整理しましょう。カスタムクラスcSkinningに基づいてオブジェクトを作成し、
## skinDataというリストに保存します。
## ドキュメントを解析し、XML形式に変換して、メモリにロードします
xml_doc = minidom.parse(path)
xml_doc.toxml()
## ルートノードを取得
joints = xml_doc.childNodes[0].childNodes
## すべてのデータを収集し、cSkinningオブジェクトをskinDataリストに保存します
for joint in joints:
if joint.localName!=None:
vertices = []
weights = []
jointData.append(joint.localName)
vertices = eval(joint.attributes.item(1).nodeValue)
weights = eval(joint.attributes.item(0).nodeValue)
skinData.append(cSkinning(vertices,joint.localName,weights))
##skinDataからアイテムをPrintすると、
##item.id、item.influence、またはitem.weightsを印刷すると、そのクラスに格納されているデータを確認できます。
##インフルエンスとウェイトはコヒーレントテーブルであるため、.weightsのインデックス15はインフルエンス15のウェイトを表すことがわかります。
##次に、スキンクラスターを作成し、すべてのジョイントを追加します。ウェイト属性を適用する前に、
##normalizeWeights属性を0に設定する必要があります。そうしないと、Mayaはウェイトを追加するたびにスキンウェイトを正規化します。
##また、skinPercentを使用してスキンをバインドする場合は、マヤの標準スキンウェイトを削除する必要があります。
skincluster = pm.animation.skinCluster(mesh,jointData)
skincluster.setNormalizeWeights(0)
pm.skinPercent(skincluster, mesh, nrm=False, prw=100)
##ウェイトを適用する前に、最後に行う必要があることが1つあります。MayaはIDに基づいてWeightを保存します。新しいスキンクラスターを追加したため、
##これらのIDは古いIDと一致しなくなりました。ここでのこのコードは、skinDataリストでそれらを更新するだけです。
tmpJoint = []
for storedJoint in jointData:
for skinnedJoint in skincluster.influenceObjects():
if skinnedJoint==storedJoint:
tmpJoint.append(skincluster.indexForInfluenceObject(storedJoint))
for each in range(0,len(skinData)):
skinData[each].influence = tmpJoint[each]
##素晴らしい、今ここに楽しい部分があります。setAttrを使用してスキンウェイトを追加しましょう。それが終わったら、スキンウェイトの正規化を再度オンにすることを忘れないでください。
for each in range(0,len(skinData)):
for vertex in range(0,len(skinData[each].id)):
vert = skinData[each].id[vertex]
weights = skinData[each].weights[vertex]
pm.setAttr('%s.weightList[%s].weights[%s]'%(skincluster,vert,skinData[each].influence),weights)
##約30kの頂点を持つメッシュの場合、約3秒かかりました。かなり許容できる約6秒の読み込み。スクリプトはMaya2012でテストされ、
##Maya 2013でこれを機能させるのに問題があると聞きましたが、残念ながらまだ調査する時間がありませんでした。聞いてくれてありがとう。
#--------------------------------------------------
class cSkinning():
def __init__(self,id,infl,weights):
##これでデータを保存できますが、ロードすることもできます。これはもう少し苦痛であることが判明しました。
##skinPercentを使用すると、スキンウェイトの設定が非常に遅くなる可能性があります。したがって、この場合、
##setAttrを使用して値を直接設定しました。これはかなり高速です。
##少しずつ見ていきましょう。まず、データを効率的に保存するためのクラスを設定します。
self.id = id
self.influence = infl
self.weights = weights
SkinWeight_To_MiniDomClass=SkinWeight_To_MiniDom()
BaseMesh="Mesh"
#cmds.select(BaseMesh)
SkinWeight_To_MiniDomClass.fnSaveSkinning(BaseMesh,"baseMesh_SkinWeight_"+BaseMesh+".txt")