2015年5月3日日曜日

スキンウエイト値を切り捨て・切り上げ・四捨五入するプロシージャ

引き続き、モデラーさん用Toolを作成してます。
表題のような機能を持ったToolの作成依頼があったので、対応してました。

最初melで処理を作ったのですが、頂点数が多いと途端に重たくなり、
えっなに、フリーズ!?...みたいな状態。。。せいぜい2000頂点とかその程度で
重たくなりました。

パイソン…やるか。。

というわけで、今回はpythonのお話です。
どっちにせよ、ゆくゆく.pyは習得するつもりで、チマチマ勉強はしていたので、
ついに日の目を見る時が来たか。。。という感じで、練習&実習がてら.pyでプロシージャを
組んでみました。

以下ソースです。

mode=1で切り捨て・mode=2で切り上げ・mode=3で四捨五入。
CUL_floatは、切り上げ・切り下げの目安の数値 ※四捨五入モードでは無視されます。
CUL_ROUNDは、小数点第何位かの指定、です。

例)
mode=1,CUL_float=2.0,CUL_ROUND=2.0で、0.02で切り上げ
mode=2,CUL_float=2.0,CUL_ROUND=1.0で、0.2で切り上げ
mode=3,CUL_float=5.0,CUL_ROUND=2.0で、0.05で切り上げ


import maya.cmds as mc
import maya.mel as mel
import math as math
def ARKW_skinWeightAdjustProc(mode=0,CUL_float=2.0,CUL_ROUND=2.0):
    skincluster=[]
    vtx_list=mc.ls(sl=True,fl=True)
    #頂点ごとの処理
    for vtx in vtx_list:
        inflnode_list=[]
        inflpct_list=[]
        new_inflpct_list=[]
        node_name=vtx.split('.')[0]
        #スキンクラスタを探す
        skincluster=mel.eval('findRelatedSkinCluster %s'%node_name)
        #スキンクラスタがなければスキップ
        if skincluster is None:
            mc.error('スキンクラスタが見当たりません。\n',sl=False)
        else:
            #インフルエンスオブジェクトのリストを取得する
            inflnode_list=mc.skinPercent(skincluster,vtx,q=True,t=None)
            #インフルエンスオブジェクトごとにスキン数値の取得
            for inflnode in inflnode_list:
                inflpct_list.append(mc.skinPercent(skincluster,vtx,t=inflnode,q=True))
            i=0
            while(i<len(inflpct_list)):
                #切り捨て
                if mode==0:
                    inflpct_list[i] = math.floor(inflpct_list[i]*10.0**CUL_ROUND)
                    REM = inflpct_list[i]%CUL_float
                    QUO = math.floor(inflpct_list[i]/CUL_float)
                    if REM>0:
                        inflpct_list[i] = CUL_float*QUO
                    inflpct_list[i] *= 1/10.0**CUL_ROUND
                #切り上げ
                elif mode==1:
                    inflpct_list[i] = math.ceil(inflpct_list[i]*10.0**CUL_ROUND)
                    REM = inflpct_list[i]%CUL_float
                    QUO = math.floor(inflpct_list[i]/CUL_float)
                    if REM>0:
                        inflpct_list[i] = CUL_float*QUO+CUL_float
                    inflpct_list[i] *= 1/10.0**CUL_ROUND
                #四捨五入            
                else:
                    inflpct_list[i] = round(inflpct_list[i],int(CUL_ROUND))
                i+=1
            #値の正規化
            i=0
            j=0
            while(i<len(inflpct_list)-1):
                if inflpct_list[i]<inflpct_list[i+1]:
                    j=i
                elif inflpct_list[i]>inflpct_list[i+1]:
                    j=i+1
                i+=1
            total_skinpct=0.0
            i=0
            while(i<len(inflpct_list)):
                total_skinpct=total_skinpct+inflpct_list[i]
                i+=1
            if total_skinpct!=1:
                inflpct_list[j] += 1-total_skinpct
            #スキンウエイトの適応
            mc.setAttr((skincluster + '.normalizeWeights'),0)
            i=0
            while (i<len(inflnode_list)):
                mc.skinPercent(skincluster,vtx,nrm=False,tv=(inflnode_list[i],inflpct_list[i]))
                i+=1
            mc.setAttr((skincluster + '.normalizeWeights'),1)
            #print (inflpct_list)
            del inflnode_list
            del inflpct_list


---------------
今回、慣れない.pyだったので、いろいろ苦労しましたが、
ポイントは3点ほどありました。

①melにしかないコマンドは、pythonからmelを呼ぶ
 #スキンクラスタを探す
 skincluster=mel.eval('findRelatedSkinCluster %s'%node_name)
↑ここですね。

②skinPercentコマンドで、transformをqモードで呼び出したいときはNoneで指定する
#インフルエンスオブジェクトのリストを取得する
inflnode_list=mc.skinPercent(skincluster,vtx,q=True,t=None)
↑これですね。。。
ここは結構詰まりました。
melでの書き方では…
string $inflnode_list[]=`skincluster -q -t skincluster vtx`;
…ですので、最初は、
inflnode_list=mc.skinPercent(skincluster,vtx,q=True,t=True)
とか
inflnode_list=mc.skinPercent(skincluster,vtx,q=True,t='')
とか
inflnode_list=mc.skinPercent(skincluster,vtx,q=True,t=)
とか、試しましたが、すべて怒られましたw
t=None と指定しないといけなかったのですね!
参考:http://forums.cgsociety.org/archive/index.php/t-928526.html
調べたら、割と結構同じ個所で悩まれた方がいるみたいで。。
てっとり早くmelから呼び出して解決された方なんかもいらっしゃるみたいですね。。。

③ceil/floorしたfloat数値をリストに入れ、そのまま照会すると、ceil/floorされてない!
これも。。。悩みました。
たとえば、

d_list=[]

a=0.4966666
b=100.0
c=math.floor(a*b)
d_list.append(c/b)

print (d_list)

print (d_list[0])

・・・を実行すると、

[0.48999999999999999]
0.49

・・・という結果が返ってきます。
リストに入れたままprintすると、近似値が返ってくるんですね。。。
ちゃんと数値を扱いたい場合は、取り出してあげないといけないのかな??

pythonのドキュメントを呼んでいたところ、
Python は格納されている値の10進小数での近似値を表示するので、格納されている値が元の10進小数の近似値でしか無いことを忘れがちです。」http://docs.python.jp/2/tutorial/floatingpoint.html
・・・との表記が。
これは仕様なのかな。。


ともあれ、なんとか形にできそう。
Pythonは、文法自体はすごくわかりやすいし、いろいろてっとり早かったりするので、
慣れると作業が速くなるかもしれません。処理も早くなるし、APIにもアクセスできるし、
他にもいろいろなモジュールがあるから、便利だし。。。
pythonはいいことづくしという感じでしょうか。

python開発の、よい一歩になりました。