Maya Script Pymel API2.0 – ピボットの修正2

以前modoからモデルデータをFBXなどでMayaに持っていった後に、
各ジオメトリのピボットをバウンディングボックスの中心に設定するスクリプトを書きました。
しかし、使ってみると色々と修正するべき点があったので、それを修正しました。

import pymel.core as pm

def set_bb_center_pymel():
	sel = [x for x in pm.ls(sl=1,type=pm.nt.Transform) if x.getShape() and type(x.getShape())==pm.nt.Mesh]
	if sel:
		for s in sel:
			bbc = s.getBoundingBox().center()
			s.setTranslation([-x for x in bbc],"world")
			pm.makeIdentity(s,a=1)
			s.zeroTransformPivots()
			s.setTranslation(bbc,"world")
set_bb_center_pymel()

Pymelでやるとこんな感じです。
Pymelのメソッドを使いまくって、簡単に書いております。
バウンディングボックスの中心点を求め、それにマイナスした値で移動させると、ジオメトリが原点に行きます。
その後、トランスフォームのフリーズを行い、ピボットをゼロにして、以前求めたバウンディングボックスの中心点に
ジオメトリを移動させて終了です。

 

これをAPI2.0でやるとどうなるのだろうか、と思い立ったので、やってみました。

import maya.api.OpenMaya as om2

def set_bb_center_om2():
	selList = om2.MGlobal.getActiveSelectionList()

	if not selList:
		return

	selObj = []
	selMesh = []
	
	for x in range(selList.length()):
		try:
			mDag,mCmp = selList.getComponent(x)
		except:continue
		
		if mDag.hasFn(om2.MFn.kMesh):
			selObj.append(om2.MFnTransform(mDag))
			selMesh.append(om2.MFnMesh(mDag))

	if selObj:
		for t,m in zip(selObj,selMesh):
			pCenter = t.translation(om2.MSpace.kWorld)
			mPoints = m.getPoints(om2.MSpace.kWorld)
			xPoints = []
			yPoints = []
			zPoints = []
			[[xPoints.append(x.x),yPoints.append(x.y),zPoints.append(x.z)] for x in mPoints]
			#bBox = [[min(xPoints),min(yPoints),min(zPoints)],[max(xPoints),max(yPoints),max(zPoints)]]
			bBoxCenter = om2.MVector(sum(xPoints)/len(xPoints),sum(yPoints)/len(yPoints),sum(zPoints)/len(zPoints))
			if (round(pCenter.x,4),round(pCenter.y,4),round(pCenter.z,4)) == (round(bBoxCenter.x,4),round(bBoxCenter.y,4),round(bBoxCenter.z,4)):
				continue
			m.setPoints([x.__isub__(bBoxCenter) for x in mPoints],om2.MSpace.kWorld)
			t.setTranslation(bBoxCenter,om2.MSpace.kWorld)
set_bb_center_om2()

こんな感じになりました。
やっている手順は同じです。ただ、注意しなくてはならない点は、プラグイン化していないので、取り消しが効きません。
処理が簡単なので、Pymelでやってもさほど重さは感じないので、Pymelを使ったほうが良いかと思います。

 

APIにはMBoundingBoxというクラスがあるので、それを使ってみようかと思ったのですが、Mpointの最小値と最大値を渡して作るようで、
それなら自分で求めたほうが楽かなぁ、と使いませんでした。コメントアウトされている箇所は、バウンディングボックス自体を求めております。
この辺りもPythonの組み込み関数に頼りっぱなしです。

 

APIではトランスフォームノードだけでなく、メッシュノード自体にも編集を加えます。
メッシュ全体の頂点を直接移動させています。本当に正しいやり方かは知りません。
色々とやってみたのですが、トランスフォームノードだけではどうにもなりませんでした。
行列にもちょっと手を出してしまいました。
最終的には頂点を直接編集する方法を選びました。さすがにAPIでもそれは遅いだろう。と思っていたのですが、
それでもPymelより早いです。

 

中々スキニングクラスターの編集には至りませんが、APIで色々と作って慣れてゆくしか方法はなさそうです。
でもなんでこんなに情報が少ないのだろうか?結局1.0の方ができることが多いのだろうか?
まぁ、その辺も含めてとにかくやってみて知ってゆこう。

Maya Script Pymel – ポーズコピーツール

リグの付いていないジョイントに対して、移動、回転、スケール値をコピーしたい。
という必要がありました。
以前そんな感じのツールを作ったなぁ、と使ってみると、使えない、、
キーが打たれていないとダメそうだったので、キーを打って再実行。
やはり使えない、、酷い、、
こんなツールを上げていたとは、、

 

で、自分の作ったツールを解析するよりも、新たに作ってしまおう。
と、キーのあるなしに関係なく、アトリビュートの値をコピーするだけのツールを作りました。
折角なので、ガワの部分は以前の物をちょっと拝借しました。

import pymel.core as pm

class PoseCopyTool(object):
	def __init__(self):
		self.NAME = "_pose_copy_tool"
		self.TITLE = "Pose Copy Tool"
		self.WIN = ""
	
	def create(self):
		try:
			pm.deleteUI(self.NAME)
		except:pass
		
		with pm.window(self.NAME,t=self.TITLE,w=240,h=100) as self.WIN:
			with pm.columnLayout(adj=1):
				with pm.rowLayout(nc=3,cw3=[70,80,70]):
					self.MirCB = pm.checkBox(l="Mirror",v=1,cc=pm.Callback(self.check_box_cb))
					self.hirCB = pm.checkBox(l="Hierarchy",v=0)
					self.wolCB = pm.checkBox(l="World",v=0)
				with pm.rowLayout(nc=2,cw2=[90,180]):
					self.tText = pm.text(l="Flip Transrate")
					self.cbT = pm.checkBoxGrp(ncb=3,la3=["X","Y","Z"],va3=[1,0,0],cw3=[60,60,60])
				with pm.rowLayout(nc=2,cw2=[90,180]):
					self.rText = pm.text(l="Flip Rotate")
					self.cbR = pm.checkBoxGrp(ncb=3,la3=["X","Y","Z"],va3=[1,0,0],cw3=[60,60,60])
				with pm.rowLayout(nc=2,cw2=[90,180]):
					self.sText = pm.text(l="Flip Scale")
					self.cbS = pm.checkBoxGrp(ncb=3,la3=["X","Y","Z"],va3=[1,0,0],cw3=[60,60,60])
				pm.button(l="Apply",c=pm.Callback(self.main_cb))
	
	def check_box_cb(self):
		if self.MirCB.getValue():
			self.tText.setLabel("Flip Transrate")
			self.rText.setLabel("Flip Rotate")
			self.sText.setLabel("Flip Scale")
		else:
			self.tText.setLabel("Transrate")
			self.rText.setLabel("Rotate")
			self.sText.setLabel("Scale")
			
	def main_cb(self):
		a = 0.5
		sel = [x for x in pm.ls(os=1) if type(x) == pm.nt.Joint or type(x) == pm.nt.Transform]
		if len(sel)!=2:
			pm.system.displayWarning("Select 2 JointNode or TransformNode")
			return
		if self.hirCB.getValue():
			sel = [[x,y] for x,y in zip(pm.ls(sel[0],dag=1,type=pm.nt.Transform),pm.ls(sel[1],dag=1,type=pm.nt.Transform))]
		else:
			sel = [sel]
		for par in sel:
			src,tgt = par[0],par[1]
			if self.wolCB.getValue():
				srcTra = src.getTranslation("world")
			else:
				srcTra = src.getTranslation()
			srcRot = src.getRotation()
			srcSca = src.getScale()
			[tgt.sx.unlock(),tgt.sy.unlock(),tgt.sz.unlock(),tgt.tx.unlock(),tgt.ty.unlock(),tgt.tz.unlock(),tgt.rx.unlock(),tgt.ry.unlock(),tgt.rz.unlock()]
			if self.MirCB.getValue():
				cbValT = [cmp(a,self.cbT.getValue1()),cmp(a,self.cbT.getValue2()),cmp(a,self.cbT.getValue3())]
				cbValR = [cmp(a,self.cbR.getValue1()),cmp(a,self.cbR.getValue2()),cmp(a,self.cbR.getValue3())]
				cbValS = [cmp(a,self.cbS.getValue1()),cmp(a,self.cbS.getValue2()),cmp(a,self.cbS.getValue3())]
				if self.wolCB.getValue():
					tgt.setTranslation([x*y for x,y in zip(srcTra,cbValT)],"world")
				else:
					tgt.setTranslation([x*y for x,y in zip(srcTra,cbValT)])
				tgt.setRotation([x*y for x,y in zip(srcRot,cbValR)])
				tgt.setScale([x*y for x,y in zip(srcSca,cbValS)])
			else:
				if self.wolCB.getValue():
					tgtTra = tgt.getTranslation("world")
				else:
					tgtTra = tgt.getTranslation()
				tgtRot = tgt.getRotation()
				tgtSca = tgt.getScale()
				cbValT = [self.cbT.getValue1(),self.cbT.getValue2(),self.cbT.getValue3()]
				cbValR = [self.cbR.getValue1(),self.cbR.getValue2(),self.cbR.getValue3()]
				cbValS = [self.cbS.getValue1(),self.cbS.getValue2(),self.cbS.getValue3()]
				if self.wolCB.getValue():
					tgt.setTranslation([y if z else x for (x,y,z) in zip(tgtTra,srcTra,cbValT)],"world")
				else:
					tgt.setTranslation([y if z else x for (x,y,z) in zip(tgtTra,srcTra,cbValT)])
				tgt.setRotation([y if z else x for (x,y,z) in zip(tgtRot,srcRot,cbValR)])
				tgt.setScale([y if z else x for (x,y,z) in zip(tgtSca,srcSca,cbValS)])
				

pct = PoseCopyTool()
pct.create()

作っていて思い出したのは、cmp関数、これを作っている時にこんな処理ができたらなぁ、と思っていたのを思い出しました。
状況に合わせて1か-1を掛けたいときにとても便利。
ミラーしない時はチェックボックスの値を使って三項演算子で処理を切り替え。
変数の名前をもっと分かりやすく書くべきだ。と自分で突っ込みながらも、面倒なのでこれでよし。
また気が向くことがあったら、その辺はやろう。

Maya Script API2.0 – 選択範囲を頂点に変更

前回の記事でMayaコマンドを使い、コンポーネントのインデックスを取得する方法を書きましたが、
恐らく正しく動作しないかと思います。
インデックスを取得するために文字列を加工し、その後加工した文字列を整数に変換しないとインデックスとしては使えないかと思います。
なので、set集合を作る際にint()を使って整数にすれば、正しく使えるはずです。

 

じゃあ訂正しろよ、という話ですが、面倒なのでしません。
さらにはそんなややこしいやり方をする必要がないからです。

 

API2.0を使った、選択しているコンポーネントの取得の方法が分かりました。
セレクションリストの、getComponentメソッドを使うと、DAGノードとコンポーネントをMObjectとして取得することが出来ました。
取得したコンポーネントを、関数セットMFnSingleIndexedComponentに入れて、getElementsを使うとインデックスが取得できます。
それが正しいやり方かは知りませんがとにかく出来ました。

 

で、それを使って少し実用的な処理を作成。

import maya.api.OpenMaya as om2
import maya.cmds as cmds

def select_vertex_om2():

	selList = om2.MGlobal.getActiveSelectionList()

	if not selList:
		return

	selObj = {}
	for x in range(selList.length()):
		try:
			mDag,mCmp = selList.getComponent(x)
		except:continue

		if mDag.hasFn(om2.MFn.kMesh):

			selId = {}
			cmpType = None

			if mCmp.hasFn(om2.MFn.kMeshVertComponent):
				cmpType = "vtx"
			elif mCmp.hasFn(om2.MFn.kMeshEdgeComponent):
				cmpType = "edge"
			elif mCmp.hasFn(om2.MFn.kMeshPolygonComponent):
				cmpType = "face"

			if cmpType:
				selId[cmpType] = om2.MFnSingleIndexedComponent(mCmp)

			selObj[om2.MFnMesh(mDag)] = selId

	if selObj:
		cmds.select(cl=1)
		
		for k,v in selObj.items():
			if "vtx" in v:
				id = v["vtx"].getElements()
			elif "edge" in v:
				eid = v["edge"].getElements()
				eSet = []
				[eSet.extend(k.getEdgeVertices(x)) for x in eid]
				id = list(set(eSet))
			elif "face" in v:
				fid = v["face"].getElements()
				fSet = []
				[fSet.extend(k.getPolygonVertices(x)) for x in fid]
				id = list(set(fSet))
			else:
				id = range(k.numVertices)

			cmds.select(["{0:}.vtx[{1:}]".format(k.fullPathName(),x) for x in id],add=1)

select_vertex_om2()

選択されているものがメッシュでエッジやフェースが選択されていると、それらを含む頂点を選択します。
コンポーネントが選択されていないと、メッシュ全体が選択されます。

 
頂点に何かの作用をさせるスクリプトを作るときに便利です。
API2.0を使った頂点カラーエディタとかその気になれば、作ることができます。
ただ、いまさら頂点カラーかよ。とテンションは上がりません。

 

スキニングは、API1.0にある関数セットMFnSkinClusterの役割を果たす機能を見つけられません、、
恐らくより汎用的な感じで何かを使うのだろうが、まだ分からない。

 

うーん、本当はfusionmeshやSubstanceDesignerやSubstancePainterについての備忘録を書いておきたいのだが、
ついついAPI2.0をやってしまう。未開の地を探検しているようで面白い。

Maya Script API2.0 – シーン中のノードを取得

色々と調べてみても、API2.0を使った任意のノードの取得の仕方が分からない。
getActiveSelectionListで、現在の選択を取得するか、getSelectionListByNameで名前を元にセレクションリストを
作成するくらいの事しか、書かれていない。
そもそもMItがないのでどうすることもできないし、API1.0との混在もできないのでどうしようもない。
で、考えた苦肉の策が、

import maya.OpenMaya as om
import maya.api.OpenMaya as om2

def get_mesh_om():
	dagPath = om.MDagPath()
	dagIter = om.MItDag(om.MItDag.kDepthFirst, om.MFn.kMesh)
	fnMash = []
	
	selList = om2.MSelectionList()
	print "{0:-^50}".format("API1.0")
	##API1.0
	while not dagIter.isDone():
		dagIter.getPath(dagPath)
		dagNodeFn = om.MFnDagNode(dagPath)
		if dagPath.hasFn(om.MFn.kMesh):
			fnM = om.MFnMesh(dagPath)
			selList.add(fnM.fullPathName())
			print fnM.fullPathName()
		dagIter.next()
	##API1.0
	
	print "{0:-^50}".format("API2.0")
	##API2.0
	for x in range(selList.length()):
		mDag = selList.getDagPath(x)
		if mDag.hasFn(om2.MFn.kMesh):
			fnMash = om2.MFnMesh(mDag)
			print fnMash.fullPathName()
	##API2.0
get_mesh_om()

自分で書いていて、ホントかよ、と突っ込んでしまったが、API1.0で所得した物を文字列として2.0に渡している。
DAGノードであれば、これでいいが、DGノードの場合はそうも行かない。
なので、こんな感じ。

import maya.api.OpenMaya as om2
import maya.cmds as cmds

def get_material_om(args):
	selList = om2.MSelectionList()
	[selList.add(x) for x in args]
	for x in range(selList.length()):
		mDg = selList.getDependNode(x)
		if mDg.hasFn(om2.MFn.kLambert):
			fn = om2.MFnDependencyNode(mDg)
			print fn.name()

get_material_om(cmds.ls(mat=1))

Mayaコマンドで取得したものを文字列として2.0に渡している。
因みにこれではランバートを指定していても、PhongやBlinnもリストされる。
それらはランバートが元になっているからだろうか?良く分からない。

 

その場合は、渡すときにMayaコマンドで選別しておいて渡すべきだろうか?
もうそこまで考えると、頭がおかしくなりそうだ、、
うーん、何か方法がありそうだけど全くわからない。

 

そこで、Mayaコマンドで情報を取得してAPIに渡すときに役に立ちそうな機能。

import maya.cmds as cmds

selDict = {}
sel = cmds.ls(sl=1,fl=1)
vtxs = cmds.ls(cmds.polyListComponentConversion(sel,tv=1),fl=1,l=1)
for v in vtxs:
	name = v.split(".",1)[0]
	selDict[name] = set([x[x.rindex("[")+1:-1] for x in vtxs if name == x.split(".",1)[0]])
print selDict

こうすることにより選択をノードの名前とコンポーネントのインデックスに分けた辞書を作ることができる。
インデックスはset集合にしているので恐らく符合させるときに早く処理できるだろう。
しかし、全くスマートではない。

 

本当にAPI2.0だけでできないのかなぁ、、

Maya Script AIP2.0 – ハードエッジの選択やクリースエッジの選択

少しずつMayaAPI2.0に付いて分かりだしてきたので、ツールを作ってみました。
まずはメッシュノードのエッジの数をrenge関数に入れてイテレーションするようなもの。

import maya.api.OpenMaya as om2
import maya.cmds as cmds

def select_soft_or_hard_edges(flag = "hard"):

	selList = om2.MGlobal.getActiveSelectionList()

	if not selList:
		return

	selObj = []
	for x in range(selList.length()):
		mDag = selList.getDagPath(x)
		
		if mDag.hasFn(om2.MFn.kMesh):
			selObj.append(om2.MFnMesh(mDag))

	if selObj:
		if flag == "soft":
			cmds.select(["{0:}.e[{1:}]".format(x.fullPathName(),i) for x in selObj for i in range(x.numEdges) if x.isEdgeSmooth(i)],r=1)
		elif flag == "hard":
			cmds.select(["{0:}.e[{1:}]".format(x.fullPathName(),i) for x in selObj for i in range(x.numEdges) if not x.isEdgeSmooth(i)],r=1)

select_soft_or_hard_edges()

実行すると選択しているノードの内、メッシュノードのハードエッジだけを選択します。
どうやらAPI2.0や1.0では実行はMayaコマンドに投げてしまうのが多い。
APIで実行すると取り消しが効かなくなるため、プラグイン化をしなくてはならない。
なので、簡単な実行であれば、Mayaコマンドを使うのだろう。
 

そして、クリースエッジ。

import maya.api.OpenMaya as om2
import maya.cmds as cmds

def select_crease_edges():

	selList = om2.MGlobal.getActiveSelectionList()

	if not selList:
		return

	selObj = []
	for x in range(selList.length()):
		mDag = selList.getDagPath(x)
		
		if mDag.hasFn(om2.MFn.kMesh):
			selObj.append(om2.MFnMesh(mDag))

	if selObj:
		hasCmesh = {}
		for s in selObj:
			cEdges = []
			try:
				cEdges = s.getCreaseEdges()
			except:pass
			if cEdges:
				hasCmesh[s.fullPathName()] = cEdges[0]
		if hasCmesh:
			cmds.select(["{0:}.e[{1:}]".format(k,x) for k,v in hasCmesh.items() for x in v],r=1)

select_crease_edges()

getCreaseEdgesメソッドは、そもそもクリースエッジを設定してないものだと、エラーが帰ってくるので、try文で取得を試みています。

 

どちらもMayaコマンドだけで作るよりも相当早く実行できます。
それでいながら書く行数が少ないのがAPI2.0の利点のようです。

Python cmp関数 filecmpモジュール

備忘録
cmp関数は入力された2つの値を比べて、大きさの違いを見て値を返す関数。

a = 0
print cmp(a, 0)
print cmp(a, 1)
print cmp(a, -1)

のように使うらしい。
sorted関数で使われているようだ。
以前そんな感じの機能を探していたように思うが、その時は使わなかった。
どのようにしたかは忘れたが、そんな場面に遭遇したら、試しに使ってみよう。

 

そして、filecmpモジュール。

import filecmp

print filecmp.cmp("E:/text.txt","E:/text2.txt")

名前や更新日が違うものでも、内容が同じであれば、「True」を返してくる。
Pythonは便利なモジュールが沢山ある。

Maya Script API2.0 メッシュノードの取得

MayaAPI2.0で選択しているメッシュノードを取得する方法です。

import maya.api.OpenMaya as om2

def get_mesh_node():
	selectionLs = om2.MGlobal.getActiveSelectionList()
	selObj = []
	selDnode = []

	for x in range(selectionLs.length()):
		mDag = selectionLs.getDagPath(x)
		if mDag.hasFn(om2.MFn.kMesh):		
			selObj.append(om2.MFnMesh(mDag))
	for x in selObj:
		print x.name()
get_mesh_node()

1.0に比べるとはるかに簡単なコードを書くことができます。
ただ、英語でも情報が少ないので調べるのが難しい、、

 

頂点のインデックスを取得するには、
MFnMesh関数にセットしたものに対して、「getVertices」メソッドを実行すると、
メッシュの各頂点の情報が取得できるようです。
2つのタプルで、一つ目がその頂点で構成されるポリゴンが何角形であるか、もう一つが
頂点のフェースインデックス。

 
うーん、フェースインデックスとは、、
頂点インデックスとして使う場合は、set()関数でまとめて使えば良い、ということだろうか。
頂点インデックスをMObjectとして取得できるもっと良いメソッドが存在するのだろうか?
それすらも分からない。

 

それとシェイプノードからスキンクラスターにたどり着く方法もまだ分からない。

 

あれ、記事を書きながらMayaで試していたら、
「getActiveSelectionList」で取得したものはコンポーネントであれば、コンポーネントとして取得している。
あら、凄い。これは便利だ。

Maya Script OpenMaya ウェイトのインポート・エクスポート

えーと、事の経緯は中々複雑です、、
sqlite3というPythonのデータベースモジュールの使い方を知りました。
そのモジュールには、メモリ内にデータベースを作成する。という機能があります。

 

スキニングを一旦外して再びバインドしたい。という場面に遭遇し、
以前作成したツールでウェイト情報をやりとりしていたのですが、重い。
メモリ内にデータがあれば、もっと早くなるのではないか?
と考え早速作ってみました。

 

しかし、重い、、読み込みは大した問題では無いようで、
そもそもskinPercentコマンドが実行に時間がかかるようです。

 

更に色々と調べていると、OpenMayaでスキニングウェイト情報のやりとりをしているスクリプトを発見。
それを改造して実装してみました。
拍子抜けするくらいに早い。
数千ポリゴンならば、待つ必要もない。数万ポリゴンでもほんの数秒。
バカらしい、、

 

更に、ファイルに保存する必要がないからその機能はつけていなかったのですが、
何となくPickleを使ってそれも実装してみました。
これもcPickleを使ったので、驚異的な早さだ。
しかもPythonのオブジェクトをそのままの状態で持ってくれるので、データを読み込んだ後に加工の必要がない。
こんなに便利だとは知らなかった、、
今までテキストに書き込んだり、読み込んだりで手を加えていたのがバカバカしくなる。
最も、テキストエディタで編集できる。という点では利点はある。

 

そしてさらに、webに公開されいている情報のほとんどがAPI1.0のもので、中々2.0の情報を見つけることができない。
人によっては2.0よりも1.0の方が速度面で優れいてる。という記述も見つかります。
ただ、コードのシンプルさ、開発のやりやすさを考えると、やはり2.0の方が有利だ。
2.0はPymelに似た感覚で使うことができる。速度は1.0とくらべて同じかちょっと遅いか、なのかな?
早いと聞いていたのでちょっとがっかりですが、簡単な方が良い。

 

ウェイトインポートエクスポートのスクリプトは以下です。

# -*- coding: utf-8 -*-
try:
	import cPickle as pickle
except:
	import pickle
import pymel.util.path as pmp
import maya.OpenMaya as OpenMaya
import maya.OpenMayaAnim as OpenMayaAnim
import maya.cmds as cmds
import pymel.core as pm

class Weight_OM(object):
	def __init__(self):
		self.crean_data()
		self.Name = "_save_weight_om"
		self.Win = ""
		self.Title = u"スキニングウェイト インポートエクスポート"
	
	def create(self):
		try:
			pm.deleteUI(self.Name)
		except:pass
		with pm.window(self.Name,t=self.Title) as self.Win:
			with pm.columnLayout(adj=1):
				pm.button(l=u"メモリに記憶",c=pm.Callback(self.memory_data))
				pm.button(l=u"記憶を適用",c=pm.Callback(self.load_data))
				pm.separator()
				pm.button(l=u"クリア",c=pm.Callback(self.crean_data))
				pm.button(l=u"確認",c=pm.Callback(self.check_memory))
				pm.separator()
				pm.button(l=u"ファイルに書き込み",c=pm.Callback(self.file_io,"write"))
				pm.button(l=u"ファイルの読み込み",c=pm.Callback(self.file_io,"read"))
	
	def file_io(self,flag):
		prj = pmp(pm.workspace(q=1,act=1))
		if prj:
			if flag == "read":
				sfiles = pm.fileDialog2(ds=2,cap=u"スキニングウェイトインポート",dir=prj,fm=4,ff="*.skn",okc=u"開く")
				if sfiles:
					sfiles = [pmp(x) for x in sfiles]
					for sfile in sfiles:
						with open(sfile,"rb") as f:
							[self.memShapes.update({k:v}) for k,v in pickle.load(f).items()]
			else:
				if not self.memShapes:
					pm.system.displayWarning(u"保存すべきデータがありません")
					return
				dir = pm.fileDialog2(ds=2,cap=u"スキニングエクスポート",dir=prj,fm=3,okc=u"選択")
				if dir:
					dir = pmp(dir[0])
					with open(dir.joinpath(prj.namebase + ".skn"),"wb") as f:
						pickle.dump(self.memShapes,f,2)

	def crean_data(self):
		self.memShapes = {}
	
	def memory_data(self):
		objNames = cmds.ls(sl=1,o=1)
		if objNames:
			for objName in objNames:
				hitorys = cmds.listHistory(objName)
				shapeNames = cmds.ls(hitorys,type="shape")
				if shapeNames:
					shapeName = shapeNames[0]
					sCluster = cmds.ls(hitorys,type="skinCluster")
					if sCluster:
						sCluster = sCluster[0]
						selList = OpenMaya.MSelectionList()
						selList.add(sCluster)
						clusterNode = OpenMaya.MObject()
						selList.getDependNode(0, clusterNode)
						skinFn = OpenMayaAnim.MFnSkinCluster(clusterNode)

						infDags = OpenMaya.MDagPathArray()
						skinFn.influenceObjects(infDags)

						infIds = {}
						infs = {}
						for x in range(infDags.length()):
							infPath = infDags[x].fullPathName()
							infId = int(skinFn.indexForInfluenceObject(infDags[x]))
							infIds[infId] = x
							infs[infId] = infPath

						wlPlug = skinFn.findPlug("weightList")
						wPlug = skinFn.findPlug("weights")
						wlAttr = wlPlug.attribute()
						wAttr = wPlug.attribute()
						wInfIds = OpenMaya.MIntArray()

						weights = {}
						num = wlPlug.numElements()
						#cmds.progressWindow(t=u"ウェイトエクスポート",ii=1,min=0,max=num,pr=0)
						for vId in range(num):
							vWeights = {}
							wPlug.selectAncestorLogicalIndex(vId, wlAttr)
							wPlug.getExistingArrayAttributeIndices(wInfIds)

						#	if cmds.progressWindow(q=1,ic=1):
						#		cmds.progressWindow(ep=1)
						#		return
						#	cmds.progressWindow(e=1,pr=vId)

							infPlug = OpenMaya.MPlug(wPlug)
							for infId in wInfIds:
								infPlug.selectAncestorLogicalIndex(infId, wAttr)
								try:
									vWeights[infs[infId]] = infPlug.asDouble()
								except KeyError:
									pass
							weights[vId] = vWeights
						#cmds.progressWindow(ep=1)

						self.memShapes[shapeName] = weights

	def load_data(self):
		objNames = cmds.ls(sl=1,o=1)
		if objNames:
			for objName in objNames:
				hitorys = cmds.listHistory(objName)
				shapeNames = cmds.ls(hitorys,type="shape")
				if shapeNames:
					shapeName = shapeNames[0]
					sCluster = cmds.ls(hitorys,type="skinCluster")
					if sCluster and shapeName in self.memShapes:
						sCluster = sCluster[0]
						selList = OpenMaya.MSelectionList()
						selList.add(sCluster)
						clusterNode = OpenMaya.MObject()
						selList.getDependNode(0, clusterNode)
						skinFn = OpenMayaAnim.MFnSkinCluster(clusterNode)

						infDags = OpenMaya.MDagPathArray()
						skinFn.influenceObjects(infDags)

						infs = {}
						inWeight = {}
						for x in range(infDags.length()):
							infPath = infDags[x].fullPathName()
							infId = int(skinFn.indexForInfluenceObject(infDags[x]))
							infs[infId] = infPath
							inWeight[infPath] = infId

						for inf in infs.values():
							cmds.setAttr("{0:}.liw".format(inf))

						skinNorm = cmds.getAttr("{0:}.normalizeWeights".format(sCluster))
						if skinNorm:
							cmds.setAttr("{0:}.normalizeWeights".format(sCluster),0)
						cmds.skinPercent(sCluster,shapeName,nrm=False,prw=100)

						if skinNorm:
							cmds.setAttr("{0:}.normalizeWeights".format(sCluster),skinNorm)

						wlPlug = skinFn.findPlug("weightList")
						wPlug = skinFn.findPlug("weights")
						wlAttr = wlPlug.attribute()
						wAttr = wPlug.attribute()

						num = wlPlug.numElements()
						
						#--ここから--
#						cmds.progressWindow(t=u"ウェイトインポート",ii=1,min=0,max=num,pr=0)
#						for vId in range(num):
#							if cmds.progressWindow(q=1,ic=1):
#								cmds.progressWindow(ep=1)
#								return
#							[cmds.setAttr("{0:}.weightList[{1:}].weights[{2:}]".format(sCluster,vId,inWeight[infDag]),infValue) for infDag, infValue in  sorted(self.memShapes[shapeName][vId].items())]
#							cmds.progressWindow(e=1,pr=vId)
#						cmds.progressWindow(ep=1)
						#--ここまで--
						
						#--この下の行を削除--
						[cmds.setAttr("{0:}.weightList[{1:}].weights[{2:}]".format(sCluster,vId,inWeight[infDag]),infValue) for vId in range(num) for infDag, infValue in  sorted(self.memShapes[shapeName][vId].items())]

	def check_memory(self):
		if self.memShapes:
			cmds.confirmDialog(m=u"\n".join(self.memShapes.keys()),t=u"メモリに記憶されている情報",b="OK",db="OK",cb="OK",ds="OK")
		else:
			cmds.confirmDialog(m=u"何もありません",t=u"メモリに記憶されている情報",b="OK",db="OK",cb="OK",ds="OK")


wdb = Weight_OM()
wdb.create()

コードの中でプログレスウィンドウの部分をコメントアウトしてあります。
プログレスウィンドウを表示されると遅くなります。
それでも表示させたい場合は93行目から112行目までの、「#」(シャープ)記号を消去してください。
読み込みの方はリスト内包表記を使っているので、163行目から170行目までのコメントアウトを解除し(シャープ記号の消去)、
174行目をコメントアウト、もしくは削除して実行してください。

 

使い方は、
「メモリに記憶」スキニングされているノードを選択して実行するとその情報がメモリに保存されます。
「記憶を適応」メモリに記憶されている情報を選択しているノードに適用します。
「クリア」メモリの内容を消去します。
「確認」メモリ内にある情報を確認します。スキニング情報を記憶させたシェイプノードの名前が表示されます。
「ファイルに書き込み」記憶されている情報があれば、ダイアログが表示され、ディレクトリを選ぶことで、
           ファイルを保存できます。ダイアログに表示されるディレクトリはプロジェクトディレクトリです。
           ディレクトリを選択すると、そのディレクトリに「プロジェクト名.skn」というファイルを作成しデータを保存します。
「ファイルの読み込み」「.skn」ファイルを読み込みます。書き込みと同じで、プロジェクトディレクトリを初期位置とした、
           ダイアログが開きます。

 

作成されるファイルにはプロジェクト単位でスキニング情報が保存されます。
こうすることにより、名前の問題が解決できた。

 

さて、手探りでAPI2.0を勉強してゆこう。

アニメーションが親切に解説されております

レンダリング、ライティングの基本が分かります

図版が見やすい美術解剖書です