edgeToEdgeLoopSkinWeightHanmer
global proc edgeToEdgeLoopSkinWeightHanmer(){
SelectEdgeLoopSp;
//polySelectSp -loop;
ConvertSelectionToVertices;
WeightHammer;
}
edgeToEdgeLoopSkinWeightHanmer()
edgeToEdgeLoopSkinWeightHanmer
global proc edgeToEdgeLoopSkinWeightHanmer(){
SelectEdgeLoopSp;
//polySelectSp -loop;
ConvertSelectionToVertices;
WeightHammer;
}
edgeToEdgeLoopSkinWeightHanmer()
Meshグループに無数にMeshがはいっててスキンウェイトをコピーするのがめんどくさくなったから書いたよ
Aのメッシュグループから
Bのメッシュグループにコピーしていく
動作はちょっと逆で
Bのグループのメッシュ名をループして
Aのグループのメッシュ名にあるか探す
あったらスキンウェイトコピー
なかったらrejectedリストに入れてます。
最後にrejectedListを表示するのでスキンウェイトしなかったメッシュのリストが出ます。
// SkinWeightCopy First Select MeshGroup to Second Select MeshGroup
string $selectedArrFUllPath[] = `ls -long -sl`;
print($selectedArrFUllPath);
string $FirstSelect=$selectedArrFUllPath[0];
string $SecondSelect=$selectedArrFUllPath[1];
string $rejectArr[];
string $inputNodes[] = `ls -type transform -long -dag $FirstSelect`;
string $destNodes[] = `ls -type transform -long -dag $SecondSelect`;
$inputNodeslong=size($inputNodes);
$destNodeslong=size($destNodes);
string $buffer[];
string $buffer_i[];
$finded = 0;
for($d = 0; $d <$destNodeslong;$d++){
string $destNode_name=$destNodes[$d];
if($destNode_name==$SecondSelect){
}else{
print("$destNode_name= "+$destNode_name+"\n");
$numTokens = `tokenize $destNode_name "|" $buffer`;
$bufferlong=size($buffer);
string $destNode_name_Short = $buffer[$bufferlong-1]+"";
print("$destNode_name_Short= "+$destNode_name_Short+"\n");
$finded = 0;
for($i = 0; $i <$inputNodeslong;$i++){
string $inputNode_name=$inputNodes[$i];
$numTokens_i = `tokenize $inputNode_name "|" $buffer_i`;
$bufferlong_i=size($buffer_i);
string $inputNode_name_Short = $buffer_i[$bufferlong_i-1]+"";
print("$inputNode_name_Short= "+$inputNode_name_Short+"\n");
//$index=`gmatch $inputNode_name $destNode_name_Short`;
//if($index > 0){
if($inputNode_name_Short==$destNode_name_Short){
//$blendShapeName = $blendshapeRet_name;
print("$destNode_name= "+$destNode_name+" isHit!! "+"\n");
select -r $inputNode_name;
select -add $destNode_name;
copySkinWeights -noMirror -surfaceAssociation closestPoint -influenceAssociation closestJoint;
//copySkinWeights -noMirror -surfaceAssociation closestPoint -influenceAssociation closestJoint -sourceSkin $inputNode_name -destinationSkin $destNode_name;
$finded = 1;
}
}
if($finded==0){
$rejectArr[size($rejectArr)] = $destNode_name;
}
}
}
print("----rejected_list---start"+"\n");
print($rejectArr);
print("----rejected_list---end"+"\n");
select -r $FirstSelect ;
select -add $SecondSelect ;
メッシュグループ内のグループを無視できるように、MeshのTransformだけをフィルターした。
// SkinWeightCopy First Select MeshGroup to Second Select MeshGroup
string $selectedArrFUllPath[] = `ls -long -sl`;
print($selectedArrFUllPath);
string $FirstSelect=$selectedArrFUllPath[0];
string $SecondSelect=$selectedArrFUllPath[1];
string $rejectArr[];
//string $inputNodes[] = `ls -type transform -long -dag $FirstSelect`;
//string $destNodes[] = `ls -type transform -long -dag $SecondSelect`;
string $inputNodes_mesh[] = `ls -type mesh -long -dag $FirstSelect`;
string $destNodes_mesh[] = `ls -type mesh -long -dag $SecondSelect`;
$inputNodes_meshlong=size($inputNodes_mesh);
$destNodes_meshlong=size($destNodes_mesh);
string $inputNodes[];
string $destNodes[];
clear $inputNodes;
clear $destNodes;
string $mesh;
string $parent;
for($d0 = 0; $d0 <$inputNodes_meshlong;$d0++){
$mesh=$inputNodes_mesh[$d0];
string $parentS[] = `listRelatives -parent -path -type transform $mesh`;
$parent=$parentS[0];
$inputNodes[size($inputNodes)] = $parent;
}
for($d0 = 0; $d0 <$destNodes_meshlong;$d0++){
$mesh=$destNodes_mesh[$d0];
string $parentS[] = `listRelatives -parent -path -type transform $mesh`;
$parent=$parentS[0];
$destNodes[size($destNodes)] = $parent;
}
print("$inputNodes= ------------------------------------------------ \n");
print($inputNodes);
print("$destNodes= ------------------------------------------------ \n");
print($destNodes);
$inputNodeslong=size($inputNodes);
$destNodeslong=size($destNodes);
string $buffer[];
string $buffer_i[];
$finded = 0;
for($d = 0; $d <$destNodeslong;$d++){
string $destNode_name=$destNodes[$d];
if($destNode_name==$SecondSelect){
}else{
print("$destNode_name= "+$destNode_name+"\n");
$numTokens = `tokenize $destNode_name "|" $buffer`;
$bufferlong=size($buffer);
string $destNode_name_Short = $buffer[$bufferlong-1]+"";
print("$destNode_name_Short= "+$destNode_name_Short+"\n");
$finded = 0;
for($i = 0; $i <$inputNodeslong;$i++){
string $inputNode_name=$inputNodes[$i];
$numTokens_i = `tokenize $inputNode_name "|" $buffer_i`;
$bufferlong_i=size($buffer_i);
string $inputNode_name_Short = $buffer_i[$bufferlong_i-1]+"";
print("$inputNode_name_Short= "+$inputNode_name_Short+"\n");
//$index=`gmatch $inputNode_name $destNode_name_Short`;
//if($index > 0){
if($inputNode_name_Short==$destNode_name_Short){
//$blendShapeName = $blendshapeRet_name;
print("$destNode_name= "+$destNode_name+" isHit!! "+"\n");
select -r $inputNode_name;
select -add $destNode_name;
copySkinWeights -noMirror -surfaceAssociation closestPoint -influenceAssociation closestJoint;
//copySkinWeights -noMirror -surfaceAssociation closestPoint -influenceAssociation closestJoint -sourceSkin $inputNode_name -destinationSkin $destNode_name;
$finded = 1;
}//if
}//for
if($finded==0){
$rejectArr[size($rejectArr)] = $destNode_name;
}//if
}//if
}//for
print("----rejected_list---start"+"\n");
print($rejectArr);
print("----rejected_list---end"+"\n");
select -r $FirstSelect ;
select -add $SecondSelect ;
このノード状態がキレイなので「bindPose1が一つだけ」キープしながらモデリング編集すること。
Genesis9 やったこと
—————————————-ここからはモデリングとする。——————————————–
Das3D 4.22 のGenesis9を
Maya2022に持ってきて
duplicateでスキンバインド外し=複製して
meshGroupのグループに
・genesis9のまつ毛や口のレイヤーも入れる(skinCluster初めから無いので)
・Marvelous Designerの服レイヤーも入れる
・highheelレイヤーも入れる
highheelの高さにhipとmeshGroupをmoveする。(オプション)
↑高さはBluePrintで変更可能
—————————————-ここまではモデリングとする。——————————————–
新しい方の骨とmeshGroupでバインド
↑ちゃんとしたバインドオプションが重要
cmds.skinBind()引数なしじゃclusterがダメ
genesis9からcopySkinWeightした
モデル編集後ファイル (自分用リンク)」
jennie9_6_v12z_v00122_dupGen9_Kihon.zip
#jennie9_6_v12z_v00122_dupGen9_Kihon__ModelEnd_SkinBindSkinWeight.py
def startSelectionToNewSkinCluster():
selectList=cmds.ls(sl=1,long=1)
print("selectList= "+str(selectList))
# 全部必要だから引数にした
Genesis9 = selectList[0]
Genesis9_Mesh = selectList[1]
Genesis9s = selectList[2]
MeshGroup = selectList[3]
#------------------------------------------------------------------------
ModelEnd_SkinBind(Genesis9s,MeshGroup)
ModelEnd_SkinWeight(Genesis9,Genesis9_Mesh,Genesis9s,MeshGroup)
#------------------------------------------------------------------------
def ModelEnd_SkinBind(Genesis9s,MeshGroup):
cmds.select(Genesis9s,MeshGroup)
#MEL (Script found in: C:/Program Files/Autodesk/Maya2022/scripts/others/newSkinCluster.mel)
#newSkinCluster " -bindMethod 0 -normalizeWeights 1 -weightDistribution 0 -mi 3 -dr 4 -rui false -ihs , multipleBindPose, 0";
#Python でMEL実行
import maya.mel
maya.mel.eval('newSkinCluster " -bindMethod 0 -normalizeWeights 1 -weightDistribution 0 -mi 3 -dr 4 -rui false -ihs , multipleBindPose, 0";')
#Python NG 'maya.cmds' has no attribute 'newSkinCluster' #
#cmds.newSkinCluster(" -bindMethod 0 -normalizeWeights 1 -weightDistribution 0 -mi 3 -dr 4 -rui false -ihs , multipleBindPose, 0")
#import pymel.core as pm #OK
#pm.mel.newSkinCluster(" -bindMethod 0 -normalizeWeights 1 -weightDistribution 0 -mi 3 -dr 4 -rui false -ihs , multipleBindPose, 0")
def ModelEnd_SkinWeight(Genesis9,Genesis9_Mesh,Genesis9s,MeshGroup):
Genesis9_Low_skincluster = Get_SkinCluster(Genesis9_Mesh)
print("Genesis9_Low_skincluster= "+str(Genesis9_Low_skincluster))
MeshList = cmds.listRelatives(MeshGroup,shapes=0,fullPath=1)
print("MeshList= "+str(MeshList))
for closet_mesh in MeshList:
closet_skinCluster = Get_SkinCluster(closet_mesh)
print("closet_skinCluster= "+str(closet_skinCluster))
# 元のスキンクラスターを服のスキンクラスターへコピースキンウェイト
cmds.copySkinWeights(
sourceSkin = Genesis9_Low_skincluster,
destinationSkin = closet_skinCluster,
noMirror=True,
influenceAssociation="oneToOne",
surfaceAssociation="closestPoint"
)
def Get_SkinCluster(LowMeshTransformLong):
# トランスフォームのシェイプノードを取得
shapeList = cmds.listRelatives(LowMeshTransformLong, shapes=True, noIntermediate=True,fullPath=True)
#print("shapeList= "+str(shapeList))
# lowのヒストリーを取得し、スキンクラスターのみ取得
historyList = cmds.listHistory(shapeList[0], pruneDagObjects=True, interestLevel=2)
#print("historyList= "+str(historyList))
skinclusterList = cmds.ls(historyList, type="skinCluster")
#print("skinclusterList= "+str(skinclusterList))
skincluster = skinclusterList[0]
#print("skincluster= "+str(skincluster))
return skincluster
startSelectionToNewSkinCluster()
完成ファイル (自分用リンク)
jennie9_6_v12z_v00127_Mouse_SW_Take2OK_Kihon_UE532ExportEnd.zip
あくまで、自分の研究例ですと
スカートで足に追従させたいものは
「膝にスキニングしたほうが作業的に効率的」です。
ジャンプや走るモーションで
前側のスカートは膝で突き破るので、
膝にスキンウェイト当てておきます。
というより
タイトスカートのスキンウェイトは、まず足にスキンウェイトを足の一部のようにスキンウェイトをコピーします。
前側の又の部分は左右の足から同じ左0.5、右0.5になるようにスキンウェイトします。
ロングタイトスカートはこれでできます。
スカートのケージモデルが膝に追従する例です。
ただ、まだ、突き破っているので、スカートモデルと体の隙間をもっと大きくした方がいいでしょう
あとは物理で動かしたい頂点は下のようにします
さらに、フレアスカートの場合は
スカートの左右と後ろ部分は、放射状にジョイントをいれヒザのスキンウェイトとグラデーションするようにスキンウェイトします。あとは、おかずさんのkawaii physics で、マニュアル通り設定します。
関連
Maya の処理スピードを向上させスキン ウェイト ペイント ツール(Paint Skin Weights Tool)を使いやすくするために、全てのスキン ウェイトが 0 であるスムーズ スキンから、ジョイントとインフルエンス オブジェクトを接続解除することができます。これにより未使用のインフルエンスがスキンのインフルエンス(Influence)リストに表示されなくなり、スキン ウェイト ペイント ツール(Paint Skin Weights Tool)が使いやすくなります。
たとえば、既定のオプションを使って、人間のスケルトンにストッキングをはいた左足をスムーズ スキニングするとします。スケルトン内のすべてのジョイントがポテンシャル インフルエンスとしてストッキングに接続されますが、ウェイトが 0 以外の値になるのは左足のストッキングに最も近いジョイントだけです。
肘を選択して回転させると、Maya はストッキングのスキンを計算して 0 以外の値をもつウェイトがないかどうかを調べます。ストッキングを未使用のインフルエンスから接続解除すると、肘とウェイトが 0 のその他のジョイントの回転を処理する必要がなくなり、パフォーマンスが向上します。
未使用のインフルエンスを除去するには
基本は
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")
SkinWeight Mapが画像じゃ精度がでないのでCSVにしたらうまくいった。
SkinWeight Map export
def GetSkinCluster(mesh):
history = cmds.listHistory(mesh)
for h in history:
if cmds.objectType(h, isType='skinCluster'):
return h
return None
def GetSkinPercent(SkinCluster,vtx,mJoint):
# vtx[100] の joint1 の weight を取得
skinWeightV=cmds.skinPercent( SkinCluster, vtx, t= mJoint, query=True, value=True)
return skinWeightV
def GetPolyCount(mesh):
return cmds.polyEvaluate(mesh, v=True)
from PySide.QtCore import QByteArray, QBuffer, QIODevice, QFile
from PySide import QtGui
jointName="neckUpper|head"
mesh="Design_model|Mesh|Face_Only_Beauty"
poly_count = GetPolyCount(mesh)
skin_cluster = GetSkinCluster(mesh)
imagePath="C:/Download/Game/AdvansedLocoMotionSystem/AnimMan/SkinWeight_neck01_to_Head/SkinWeight_002simple/render.png"
WeightArray=[]
for i in range(poly_count):
vtx_attr_name = mesh + ".vtx[" + str(i) + "]"
weightV=GetSkinPercent(skin_cluster,vtx_attr_name,jointName)
WeightArray.append(weightV)
print("WeightArray="+str(WeightArray))
lenWeightArr=len(WeightArray)
print("lenWeightArr= "+str(lenWeightArr))
width=lenWeightArr
height=1
"""
#buffer = QImage(pageSize, QImage.Format_ARGB32)
buffer = QtGui.QImage(width,height,QtGui.QImage.Format_ARGB32_Premultiplied)
def setDrowPixels(MyImage,WeightArray,width,height):
for x in range(0, width ):
for y in range(0, height ):
gValue=0
if(y==0):
gValue = WeightArray[x]
else:
gValue = WeightArray[x*y]
print("x= "+str(x)+" y= "+str(y)+" gValue= "+str(gValue))
MyImage.setPixel( x, y, QtGui.QColor( gValue, gValue, gValue ).rgb() )
return MyImage
buffer=setDrowPixels(buffer,WeightArray,width,height)
buffer.save(imagePath)
"""
CSVPath="C:/Download/Game/AdvansedLocoMotionSystem/AnimMan/SkinWeight_neck01_to_Head/SkinWeight_002simple/render.csv"
import codecs
fout = codecs.open(CSVPath, 'w', 'utf_8')
for i in range(poly_count):
line = str(WeightArray[i])+","
fout.write(line)
SkinWeight Map import
def GetSkinCluster(mesh):
history = cmds.listHistory(mesh)
for h in history:
if cmds.objectType(h, isType='skinCluster'):
return h
return None
def GetSkinPercent(SkinCluster,vtx,mJoint):
# vtx[100] の joint1 の weight を取得
skinWeightV=cmds.skinPercent( SkinCluster, vtx, t= mJoint, query=True, value=True)
return skinWeightV
def SetSkinPercent(SkinCluster,vtx,mJoint,skinWeightV):
# vtx[100] の weight を. joint を指定して設定
cmds.skinPercent( SkinCluster, vtx, transformValue=[(mJoint, skinWeightV)])
def GetPolyCount(mesh):
return cmds.polyEvaluate(mesh, v=True)
from PySide.QtCore import QByteArray, QBuffer, QIODevice, QFile
from PySide import QtGui
"""
def image_to_array(imagePath):
source = QtGui.QImage(imagePath)
# バイナリとメタデータを読み込み
#bits = source.constBits()
width = source.width()
height = source.height()
print("width= "+str(width))
print("height= "+str(height))
'''
return array of integer 0-255 rgba values
[(r, g, b, a)]
'''
img = QtGui.QImage(width, height, QtGui.QImage.Format.Format_ARGB32)
img.load(imagePath)
colorArray = []
kidoArray=[]
for y in range(height):
for x in range(width):
color = QtGui.QColor()
color.setRgba(img.pixel(x,y))
colorArray.append(color.getRgb())
kidoArray.append(color.getRgb()[0])
return kidoArray
"""
def csv_to_array(CSVPath):
import codecs
fin = codecs.open(CSVPath, 'r', 'utf_8')
image_arr=[]
for line in fin:
#line=line.replace("\n","")
#line=line.replace("\r","")
#line=line.replace(',','')
lineArr=line.split(',')
for strV in lineArr:
if(strV!=""):
print("strV= "+strV)
floatStrV=float(strV)
image_arr.append(floatStrV)
return image_arr
jointName="ALS_Mannequin|root|pelvis|spine_01|spine_02|spine_03|neck_01|head"
mesh="ALS_Mannequin|Mesh|Face_Only_Beauty"
poly_count = GetPolyCount(mesh)
skin_cluster = GetSkinCluster(mesh)
#imagePath="C:/Download/Game/AdvansedLocoMotionSystem/AnimMan/SkinWeight_neck01_to_Head/SkinWeight_002simple/render.png"
CSVPath="C:/Download/Game/AdvansedLocoMotionSystem/AnimMan/SkinWeight_neck01_to_Head/SkinWeight_002simple/render.csv"
#image_arr=image_to_array(imagePath)
image_arr = csv_to_array(CSVPath)
print("image_arr= "+str(image_arr))
ArrLeng=len(image_arr)
for i in range(ArrLeng):
vtx_attr_name = mesh + ".vtx[" + str(i) + "]"
weightV=image_arr[i]
#SetSkinPercent(skin_cluster,vtx_attr_name,jointName,1)
SetSkinPercent(skin_cluster,vtx_attr_name,jointName,weightV)
元がこう
結果がこれ
ふーばっちりうまくいった様子。やったね!
Maya2016でSkinWhight Mapのインポートが壊れているので
SkinWeight Mapのエクスポートとインポートを自作してみた。ほんとテスト段階
SkinWeight Map export
def GetSkinCluster(mesh):
history = cmds.listHistory(mesh)
for h in history:
if cmds.objectType(h, isType='skinCluster'):
return h
return None
def GetSkinPercent(SkinCluster,vtx,mJoint):
# vtx[100] の joint1 の weight を取得
skinWeightV=cmds.skinPercent( SkinCluster, vtx, t= mJoint, query=True, value=True)
return skinWeightV
def GetPolyCount(mesh):
return cmds.polyEvaluate(mesh, v=True)
from PySide.QtCore import QByteArray, QBuffer, QIODevice, QFile
from PySide import QtGui
jointName="neckUpper|head"
mesh="Design_model|Mesh|Face_Only_Beauty"
poly_count = GetPolyCount(mesh)
skin_cluster = GetSkinCluster(mesh)
imagePath="C:/Download/Game/AdvansedLocoMotionSystem/AnimMan/SkinWeight_neck01_to_Head/SkinWeight_002simple/render.png"
WeightArray=[]
for i in range(poly_count):
vtx_attr_name = mesh + ".vtx[" + str(i) + "]"
weightV=image_arr[i]
weightV=GetSkinPercent(skin_cluster,vtx_attr_name,jointName)
WeightArray.append(weightV)
print("WeightArray="+str(WeightArray))
lenWeightArr=len(WeightArray)
print("lenWeightArr= "+str(lenWeightArr))
width=lenWeightArr
height=1
#buffer = QImage(pageSize, QImage.Format_ARGB32)
buffer = QtGui.QImage(width,height,QtGui.QImage.Format_ARGB32_Premultiplied)
def setDrowPixels(MyImage,WeightArray,width,height):
for x in range(0, width ):
for y in range(0, height ):
gValue=0
if(y==0):
gValue = WeightArray[x]
else:
gValue = WeightArray[x*y]
print("x= "+str(x)+" y= "+str(y)+" gValue= "+str(gValue))
MyImage.setPixel( x, y, QtGui.QColor( gValue, gValue, gValue ).rgb() )
return MyImage
buffer=setDrowPixels(buffer,WeightArray,width,height)
buffer.save(imagePath)
skinWeight Map import
def GetSkinCluster(mesh):
history = cmds.listHistory(mesh)
for h in history:
if cmds.objectType(h, isType='skinCluster'):
return h
return None
def GetSkinPercent(SkinCluster,vtx,mJoint):
# vtx[100] の joint1 の weight を取得
skinWeightV=cmds.skinPercent( SkinCluster, vtx, t= mJoint, query=True, value=True)
return skinWeightV
def SetSkinPercent(SkinCluster,vtx,mJoint,skinWeightV):
# vtx[100] の weight を. joint を指定して設定
cmds.skinPercent( SkinCluster, vtx, transformValue=[(mJoint, skinWeightV)])
def GetPolyCount(mesh):
return cmds.polyEvaluate(mesh, v=True)
from PySide.QtCore import QByteArray, QBuffer, QIODevice, QFile
from PySide import QtGui
def image_to_array(imagePath):
source = QtGui.QImage(imagePath)
# バイナリとメタデータを読み込み
#bits = source.constBits()
width = source.width()
height = source.height()
print("width= "+str(width))
print("height= "+str(height))
'''
return array of integer 0-255 rgba values
[(r, g, b, a)]
'''
img = QtGui.QImage(width, height, QtGui.QImage.Format.Format_ARGB32)
img.load(imagePath)
colorArray = []
kidoArray=[]
for y in range(height):
for x in range(width):
color = QtGui.QColor()
color.setRgba(img.pixel(x,y))
colorArray.append(color.getRgb())
kidoArray.append(color.getRgb()[0])
return kidoArray
jointName="ALS_Mannequin|root|pelvis|spine_01|spine_02|spine_03|neck_01|head"
mesh="ALS_Mannequin|Mesh|Face_Only_Beauty"
poly_count = GetPolyCount(mesh)
skin_cluster = GetSkinCluster(mesh)
imagePath="C:/Download/Game/AdvansedLocoMotionSystem/AnimMan/SkinWeight_neck01_to_Head/SkinWeight_002simple/render.png"
image_arr=image_to_array(imagePath)
print("image_arr= "+str(image_arr))
for i in range(poly_count):
vtx_attr_name = mesh + ".vtx[" + str(i) + "]"
weightV=image_arr[i]
#SetSkinPercent(skin_cluster,vtx_attr_name,jointName,1)
SetSkinPercent(skin_cluster,vtx_attr_name,jointName,weightV)
元がこれ
結果がこれ 値が飛んじゃってる気がする。 CSVとかのほうがいいかも
なんだかスムージング処理がいるのかもしれない。
■参考にさせてもらった記事
Maya python で, 特定の mesh 頂点の 特定の joint の skin weight の値を取得, 設定する – Qiita
https://qiita.com/syoyo/items/5252ac7244b4f40b2cac
Python API 2.0 MImage
https://forums.autodesk.com/t5/maya-programming/python-api-2-0-mimage/td-p/6340749
Python QImage.fill Examples
import maya.cmds as cmds
import pymel.core as pm
import os
class TwistJointClass:
def MainWay(self):
self.Selected_Joint_Check()
def Selected_Joint_Check(self):
# need elbowJoint select
selected = cmds.ls(sl=True,long=True)
print("01 selected= "+str(selected))
if(str(selected)=="[]"):
print("Need Select Elbow Joint!!!!!!!!!!!!!!!!!!")
else:
self.elbowJoint_to_twistjoint(selected)
self.elbowJoint_to_drivenkey(selected)
def elbowJoint_to_drivenkey(self,selected):
elbow_joint_full=selected[0]
print("02 elbow_joint_full= "+str(elbow_joint_full))
self.select_fullname(elbow_joint_full)
elbow_joint_name =self.get_fullname_to_name(elbow_joint_full)
elbow_joint_togeJointName = self.make_elbow_toge_joint(elbow_joint_full)
elbow_mode="Genesis8"
if(elbow_mode=="Genesis8"):
elbow_way_rz=".ry"# my model
else:
elbow_way_rz=".rz"# tutorial
print("elbow_way_rz= "+elbow_way_rz)
# Key Value 1
destArr=elbow_joint_name+elbow_way_rz
cmds.setAttr( destArr, 0 )
destArr=elbow_joint_togeJointName+".tx"
cmds.setAttr( destArr, 0 )
destArr=elbow_joint_togeJointName+elbow_way_rz
cmds.setAttr( destArr, 0 )
# Set DrivenKey
#setDrivenKeyframe -currentDriver lowerarm_r.rotateZ lowerarm_toge_r.translateX;
fromAttr=elbow_joint_name+elbow_way_rz
DestAttr=elbow_joint_togeJointName+'.tx'
cmds.setDrivenKeyframe( DestAttr, currentDriver= fromAttr)
#setDrivenKeyframe -currentDriver lowerarm_r.rotateZ lowerarm_toge_r.rotateZ;
fromAttr=elbow_joint_name+elbow_way_rz
DestAttr=elbow_joint_togeJointName+elbow_way_rz
cmds.setDrivenKeyframe( DestAttr, currentDriver= fromAttr)
# Key Value 2
SideWay=self.check_Left_Or_Right(elbow_joint_name)
print("64 SideWay= "+SideWay)
if(SideWay=="R"):
destArr=elbow_joint_name+elbow_way_rz
cmds.setAttr( destArr, 180 )
#setAttr "lowerarm_toge_r.translateX" 4;
destArr=elbow_joint_togeJointName+".tx"
cmds.setAttr( destArr, 4 )
if(SideWay=="L"):
destArr=elbow_joint_name+elbow_way_rz
cmds.setAttr( destArr, -180 )
#setAttr "lowerarm_toge_r.translateX" 4;
destArr=elbow_joint_togeJointName+".tx"
cmds.setAttr( destArr, -4 )
#setAttr "lowerarm_toge_r.rotateZ" 90;
#destArr=elbow_joint_togeJointName+elbow_way_rz
#cmds.setAttr( destArr, 90 )
# Set DrivenKey
#setDrivenKeyframe -currentDriver lowerarm_r.rotateZ lowerarm_toge_r.translateX;
fromAttr=elbow_joint_name+elbow_way_rz
DestAttr=elbow_joint_togeJointName+'.tx'
cmds.setDrivenKeyframe( DestAttr, currentDriver= fromAttr)
#setDrivenKeyframe -currentDriver lowerarm_r.rotateZ lowerarm_toge_r.rotateZ;
fromAttr=elbow_joint_name+elbow_way_rz
DestAttr=elbow_joint_togeJointName+elbow_way_rz
cmds.setDrivenKeyframe( DestAttr, currentDriver= fromAttr)
# reset joint rz
destArr=elbow_joint_name+elbow_way_rz
cmds.setAttr( destArr, 0 )
def check_Left_Or_Right(self,elbow_joint_name):
SideWay=""
jointName_arr=elbow_joint_name.split("_")
if(jointName_arr[0].lower()=="left"):
SideWay="L"
if(jointName_arr[0].lower()=="l"):
SideWay="L"
if(jointName_arr[1].lower()=="left"):
SideWay="L"
if(jointName_arr[1].lower()=="l"):
SideWay="L"
if(jointName_arr[0].lower()=="right"):
SideWay="R"
if(jointName_arr[0].lower()=="r"):
SideWay="R"
if(jointName_arr[1].lower()=="right"):
SideWay="R"
if(jointName_arr[1].lower()=="r"):
SideWay="R"
print("SideWay= "+SideWay)
return SideWay
def make_elbow_toge_joint(self,elbow_joint_full):
elbow_joint_name =self.get_fullname_to_name(elbow_joint_full)
elbow_joint_togeJointName=self.makeName_togejoint(elbow_joint_name)
elbow_pos=cmds.xform(elbow_joint_full,q=1,ws=1,rp=1)
print("elbow_pos=" +str(elbow_pos))
jointName2=cmds.joint( p=(elbow_pos[0], elbow_pos[1], elbow_pos[2]-3) ,absolute=True)
cmds.rename(jointName2,elbow_joint_togeJointName);
#cmds.parent( elbow_joint_togeJointName, elbow_joint_full )
cmds.makeIdentity( apply=True, t=1, r=1, s=1, n=0,pn=1,jointOrient=1)
return elbow_joint_togeJointName
def elbowJoint_to_twistjoint(self,selected):
elbow_joint_full=selected[0]
print("02 elbow_joint_full= "+str(elbow_joint_full))
hand_joint_full=self.get_one_child_node(elbow_joint_full)
print("03 hand_joint_full= "+str(hand_joint_full))
upperarm_joint_arr = cmds.listRelatives(str(elbow_joint_full),parent=True,fullPath=True)
upperarm_joint_full=upperarm_joint_arr[0]
print("04 upperarm_joint_full= "+str(upperarm_joint_full))
#shoulder_joint_arr = cmds.listRelatives(str(upperarm_joint_full),parent=True,fullPath=True)
#shoulder_joint_full= shoulder_joint_arr[0]
#print("05 shoulder_joint_full= "+str(shoulder_joint_full))
self.Elbow_Func(hand_joint_full,elbow_joint_full)
self.Hand_Func(hand_joint_full,elbow_joint_full)
#---------------------------------------------------------------------------------------
#--------------------------------- upperarm_joint_full -------------------------------
#---------------------------------------------------------------------------------------
self.UpperArm_Func(elbow_joint_full,upperarm_joint_full)
self.Shoulder_Func(upperarm_joint_full)
def Hand_Func(self,hand_joint_full,elbow_joint_full):
hand_joint_name =self.get_fullname_to_name(hand_joint_full)
hand_joint_twistJointName=self.makeName_twistjoint(hand_joint_name)
hand_pos=cmds.xform(hand_joint_full,q=1,ws=1,rp=1)
print("hand_pos=" +str(hand_pos))
jointName2=cmds.joint( p=(hand_pos[0], hand_pos[1], hand_pos[2]) ,absolute=True)
cmds.rename(jointName2,hand_joint_twistJointName);
cmds.parent( hand_joint_twistJointName, elbow_joint_full )
cmds.makeIdentity( apply=True, t=1, r=1, s=1, n=0,pn=1,jointOrient=1)
# -------------Hand CONNECT -----------------------------------------------------
fromAtt=hand_joint_name+".rotateX"
destArr=hand_joint_twistJointName+".rotateX"
cmds.connectAttr( fromAtt, destArr )
#--------------------------------------------------------------------------------
def Elbow_Func(self,hand_joint_full,elbow_joint_full):
# make joint
elbow_joint_name =self.get_fullname_to_name(elbow_joint_full)
elbow_joint_twistJointName=self.makeName_twistjoint(elbow_joint_name)
elbow_pos=cmds.xform(elbow_joint_full,q=1,ws=1,rp=1)
hand_pos=cmds.xform(hand_joint_full,q=1,ws=1,rp=1)
print("elbow_pos=" +str(elbow_pos))
print("hand_pos=" +str(hand_pos))
average_x=(elbow_pos[0]+hand_pos[0])/2
average_y=(elbow_pos[1]+hand_pos[1])/2
average_z=(elbow_pos[2]+hand_pos[2])/2
jointName=cmds.joint( p=(average_x, average_y, average_z) ,absolute=True)
cmds.rename(jointName,elbow_joint_twistJointName);
#makeIdentity -apply true -t 1 -r 1 -s 1 -n 0 -pn 1;
cmds.makeIdentity( apply=True, t=1, r=1, s=1, n=0,pn=1,jointOrient=1)
#cmds.joint( elbow_joint_twistJointName, e=True,position=(0, 0, 0) )
#-----------------elbow divide --------------------------------------------------
# 1
DivideName=elbow_joint_name+"_Divide"
cmds.shadingNode('multiplyDivide', asUtility=True, name=DivideName)
fromAtt=hand_joint_full+".rotateX"
destArr=DivideName+".input1X"
self.connectAttr_FullPath( fromAtt, destArr )
# ------------- CONNECT ----------
# 2
fromAtt=DivideName+".outputX"
destArr=elbow_joint_twistJointName+".rotateX"
self.connectAttr_FullPath( fromAtt, destArr )
# 3 ------------- setAttr --------------------------
destArr=DivideName+".input2X"
self.setAttr_FullPath( destArr, 0.5 )
def UpperArm_Func(self,elbow_joint_full,upperarm_joint_full):
#upperarm_joint_arr = cmds.listRelatives(str(elbow_joint_full),parent=True,fullPath=True)
#upperarm_joint_full=upperarm_joint_arr[0]
self.select_fullname(upperarm_joint_full)
# make joint -----------------------------------------
upperarm_joint_name =self.get_fullname_to_name(upperarm_joint_full)
upperarm_joint_twistJointName=self.makeName_twistjoint(upperarm_joint_name)
upperarm_pos=cmds.xform(upperarm_joint_full,q=1,ws=1,rp=1)
elbow_pos=cmds.xform(elbow_joint_full,q=1,ws=1,rp=1)
print("upperarm_pos=" +str(upperarm_pos))
print("elbow_pos=" +str(elbow_pos))
average_x=(elbow_pos[0]+upperarm_pos[0])/2
average_y=(elbow_pos[1]+upperarm_pos[1])/2
average_z=(elbow_pos[2]+upperarm_pos[2])/2
jointName=cmds.joint( p=(average_x, average_y, average_z) ,absolute=True)
cmds.rename(jointName,upperarm_joint_twistJointName);
#makeIdentity -apply true -t 1 -r 1 -s 1 -n 0 -pn 1;
cmds.makeIdentity( apply=True, t=1, r=1, s=1, n=0,pn=1,jointOrient=1)
#-----------------Upperarm divide2 --------------------------
#hand_joint_name=fullName_to_ShortName(hand_joint_full)
DivideName2=upperarm_joint_name+"_Divide"
cmds.shadingNode('multiplyDivide', asUtility=True, name=DivideName2)
# 1
fromAtt=upperarm_joint_name+".rotateX"
destArr=DivideName2+".input1X"
self.connectAttr_FullPath( fromAtt, destArr )
# ------------- CONNECT ----------
# 2
fromAtt=DivideName2+".outputX"
destArr=upperarm_joint_twistJointName+".rotateX"
self.connectAttr_FullPath( fromAtt, destArr )
# 3 ------------- setAttr --------------------------
destArr=DivideName2+".input2X"
self.setAttr_FullPath( destArr, -0.5 )
#-----------------Upperarm divide2 --------------------------END
def Shoulder_Func(self,upperarm_joint_full):
#upperarm_joint_arr = cmds.listRelatives(str(elbow_joint_full),parent=True,fullPath=True)
#upperarm_joint_full=upperarm_joint_arr[0]
self.select_fullname(upperarm_joint_full)
# make joint -----------------------------------------
upperarm_joint_name =self.get_fullname_to_name(upperarm_joint_full)
shoulder_joint_twistJointName=self.makeName_twistjoint("shoulder_"+upperarm_joint_name)
upperarm_pos=cmds.xform(upperarm_joint_full,q=1,ws=1,rp=1)
print("upperarm_pos=" +str(upperarm_pos))
jointName=cmds.joint( p=(upperarm_pos[0], upperarm_pos[1], upperarm_pos[2]) ,absolute=True)
cmds.rename(jointName,shoulder_joint_twistJointName);
#makeIdentity -apply true -t 1 -r 1 -s 1 -n 0 -pn 1;
cmds.makeIdentity( apply=True, t=1, r=1, s=1, n=0,pn=1,jointOrient=1)
#-----------------Upperarm divide3 --------------------------
#hand_joint_name=fullName_to_ShortName(hand_joint_full)
DivideName3="shoulder_"+upperarm_joint_name+"_Divide"
cmds.shadingNode('multiplyDivide', asUtility=True, name=DivideName3)
# 1
fromAtt=upperarm_joint_name+".rotateX"
destArr=DivideName3+".input1X"
self.connectAttr_FullPath( fromAtt, destArr )
# ------------- CONNECT ----------
# 2
fromAtt=DivideName3+".outputX"
destArr=shoulder_joint_twistJointName+".rotateX"
self.connectAttr_FullPath( fromAtt, destArr )
# 3 ------------- setAttr --------------------------
destArr=DivideName3+".input2X"
self.setAttr_FullPath( destArr, -1 )
#-----------------Upperarm divide2 --------------------------END
def select_fullname(self,fullname):
import sys
try:
cmds.select( fullname)
except OSError as err:
print("OS error: {0}".format(err))
except ValueError:
print("Could not convert data to an integer.")
except:
print("Unexpected error:", sys.exc_info()[0])
#raise
def setAttr_FullPath(self, destArr ,value):
import sys
try:
cmds.setAttr( destArr, value )
except OSError as err:
print("OS error: {0}".format(err))
except ValueError:
print("Could not convert data to an integer.")
except:
print("Unexpected error:", sys.exc_info()[0])
#raise
def fullName_to_ShortName(fullName):
shortName=""
fullName_index=fullName.find("|")
if(fullName_index==-1):
pass
else:
fullName_Arr=fullName.split("|")
fullName_Len=len(fullName_Arr)
shortName = fullName_Arr[fullName_Len-1]
print("shortName= "+shortName)
return shortName
def connectAttr_FullPath(self, fromAtt, destArr ):
import sys
try:
cmds.connectAttr( fromAtt, destArr )
except OSError as err:
print("OS error: {0}".format(err))
except ValueError:
print("Could not convert data to an integer.")
except:
print("Unexpected error:", sys.exc_info()[0])
#raise
def get_one_child_node(self,elbow_joint_full):
hand_joint_full=""
hand_joint_arr = cmds.listRelatives(str(elbow_joint_full),children=True,fullPath=True)
print("02 hand_joint_arr= "+str(hand_joint_arr))
temp_child_name=hand_joint_arr[0]
elbow_arr=elbow_joint_full.split("|")
elbow_len=len(elbow_arr)-1
elbow_last=elbow_arr[elbow_len]
print("elbow_last= "+elbow_last)
temp_arr=temp_child_name.split("|")
temp_len=len(temp_arr)-1
temp_elbowlastlen_name=temp_arr[elbow_len]
temp_elbowlastlen_plusone_name=temp_arr[elbow_len+1]
print("temp_elbowlastlen_name= "+temp_elbowlastlen_name)
if(elbow_last==temp_elbowlastlen_name):
print("same OK temp_elbowlastlen_name= "+temp_elbowlastlen_name)
print("temp_elbowlastlen_plusone_name= "+temp_elbowlastlen_plusone_name)
#for jname in temp_arr
buildStr=""
#for(int i = 0; i < elbow_len+1; i++)
for i in range(1,elbow_len+1+1):
jname = temp_arr[i]
buildStr=buildStr+"|"+jname
print("buildStr= "+buildStr)
hand_joint_full=buildStr
return hand_joint_full
def handJoint_to_twistjoint(self):
# need hand select
selected = cmds.ls(sl=True,long=True)
print("01 selected= "+str(selected))
hand_joint_full=selected[0]
print("02 hand_joint_full= "+str(hand_joint_full))
"""
hiji_joint_arr = cmds.listRelatives(str(hand_joint_full),parent=True,fullPath=True)
hiji_joint_full=hiji_joint_arr[0]
print("03 hiji_joint_full= "+str(hiji_joint_full))
"""
#hand_joint_name =self.get_fullname_to_name(hand_joint_full)
#hiji_joint_name =self.get_fullname_to_name(hiji_joint_full)
#hiji_twistJointName=self.makeName_twistjoint(hiji_joint_name)
#cmds.select(hiji_joint_full)
#cmds.insertJoint( 'joint2' )
#あとで復帰
#cmds.select(selected)
#cmds.select(hand_joint_full)
def makeName_twistjoint(self,joint_name):
joint_name_arr= joint_name.split("_")
twistJointName=joint_name_arr[0]+"_twist_"+joint_name_arr[1]
print("twistJointName= "+twistJointName)
return twistJointName
def makeName_togejoint(self,joint_name):
joint_name_arr= joint_name.split("_")
togeJointName=joint_name_arr[0]+"_toge_"+joint_name_arr[1]
print("togeJointName= "+togeJointName)
return togeJointName
def get_fullname_to_name(self,fullname):
hand_joint_full_arr=fullname.split("|")
print("04 hand_joint_full_arr= "+str(hand_joint_full_arr))
hand_joint_full_len=len(hand_joint_full_arr)
hand_joint_jointName=hand_joint_full_arr[hand_joint_full_len-1]
print("05 hand_joint_jointName= "+str(hand_joint_jointName))
return hand_joint_jointName
ClassIns= TwistJointClass()
ClassIns.MainWay()
デフォルトがかなりダメ
・複数のバインドポーズはエラーの元なのでオフ
・最大インフルエンス数が5は多すぎ3でいい。
・最大インフルエンス数の保持は 無駄にインフルエンスジョイントに影響するのでオフ
・スケルトンのカラー化 無駄いらない オフ
・作成時に非表示の選択項目を含める 非表示のメッシュもバインドしたいので オン
参考URL
http://3dcgr2lab.com/2019/11/30/easy-skin-weight-paint-tool/