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を勉強してゆこう。

コメントする

post date*

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

トラックバックする

トラックバック用URL:

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

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

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