株式会社デジタル・フロンティア-Digital Frontier

Header

Main

  • TOP
  • DF TALK
  • [Maya] Latticeを編集した後でDivisionを変えてみる

[Maya] Latticeを編集した後でDivisionを変えてみる

2014/12/1

Tag: ,

こんにちは。TD室の石田です。
今回はタイトルの通り編集した後のLatticeのDivisionを変える方法を試みてみました。

Mayaユーザーの方はご存知だと思いますが、Latticeの頂点を動かすとその後一切Division数を変更することが出来なくなります。
地味だけどイラっとくる仕様ですよね。
という訳でDivisionが本当に変更が出来ないのか検証してみました。

 
 

    ■■■■ Latticeのアトリビュートを調べてみる ■■■■

さて、変更したいからと言ってどこから調べたらいいものかと考えて…。
「そういえばmayaAscii(以下.ma)でデータの検証をしてた時にLatticeの記述に違和感があったなぁ」
と言う記憶からLatticeを保存した.maの記述を読んでみました。

createNode lattice -n "ffd1LatticeShape" -p "ffd1Lattice";
	setAttr ".cc" -type "lattice" 2 2 2 8 -0.5 -0.5 -0.5 .... ;

.ccアトリビュートにlattceType…、だと??
試しにLatticeを作ってDivisionと頂点を変えて.maに記述された文をコピペで実行してみました。
あら、不思議、先ほど保存した状態に戻っちゃった。てことは

編集した後でもDivisionが変えられる!!

話しは戻りますが、上記で「Latticeの記述に違和感があった」と書きました。
その違和感を説明したいと思います。 
meshを例にすると

setAttr ".vt[0:10]" 0 0 0 .....;

のように.maで記述されています。script操作でもやっている記述がズラズラと書かれています。
mesh.vtxを選択するとscriptEditerに「select -r pSphere1.vtx[0] ;」と出るので
scriptで動かすにはここを指定してあげればいい、と言うことは想像出来ますよね。

であればLatticeは「select -r ffd1Lattice.cc[0] ;」と出るのか?と言うとそんな訳ありません。
「select -r ffd1Lattice.pt[0][0][0] ;」と出ます。
でも.maの記述は.ccアトリビュートにsetAttrをしています。.ptにsetAttrしていません。

これは何かある…。
この.ccのデータタイプはなんなのか。と思ってmanualで調べてみると

アトリビュート名:cached (cc)
データタイプ:lattice

mesh、nurbsSurfaceなどもこういったデータ型があります。
latticeの場合はlattice型をここに入れると反映されるみたいですね。
試した方もいるかと思いますがこういったデータ型はgetAttrしても情報を取得出来ません。

mesh、nurbsSurfaceなどには残念ながらcachedのようなアトリビュートは…、
あれ、、、無いかと思ったらcachedInMeshって何ぞ??時間が無いので今度調べてみますw

話しは戻りまして、
cachedでsetAttrが出来るならgetAttrも出来るんじゃ…、と思いきや出来ませんでした。
getAttrが出来れば情報を取得してLatticeのDivisionを変えられるのに…。
という訳でscriptを書きました。

 
 

    ■■■■ lattice編集script ■■■■

#-*- coding:utf-8
import maya.cmds as cmds
import maya.mel as mel

#editDivisions('ffd1Lattice',s=4,t=4,u=4)

def editDivisions(*arg,**kwargs):
    sd = kwargs.get('sDivisions',kwargs.get('s',2))
    td = kwargs.get('tDivisions',kwargs.get('t',2))
    ud = kwargs.get('uDivisions',kwargs.get('u',2))
    ltc = arg
    if not arg:
        ltc_list = cmds.ls(cmds.ls(sl=1,dag=1,s=1),type='lattice')
        if not ltc_list:
            cmds.warning('please select lattice.')
            return
    else:
        ltc_list = cmds.ls(cmds.ls(ltc,dag=1,s=1),type='lattice')
        if not ltc_list:
            cmds.warning('please select lattice.')
            return
    
    ltc = ltc_list[0]
    p_ltc = cmds.listRelatives(ltc,p=1)[0]
    p_base,ltc_base,ltc_ffd = getltcNodes(ltc)

    #
    ## Latticeが移動していた場合、ダミーLatticeの頂点がワールド座標になるためその回避処理
    reCnctNode = cmds.listConnections(ltc_base+'.worldMatrix[0]',c=1,p=1)
    cmds.connectAttr(p_ltc+'.worldMatrix[0]',ltc_ffd+'.baseLatticeMatrix',f=1)

    #
    ## ダミーLatticeを作成して編集したいLatticeにデフォームをかける
    p_dmyLtc,dmyLtc = createDmyLtc(p_ltc,sd,td,ud)
    cmds.lattice(ltc,e=1,g=dmyLtc)

    #
    ## Latticeの頂点全座標を取得する。
    ltcPts = cmds.ls(dmyLtc+'.pt[*]',fl=1)
    resultPoints = []
    for ltcPt in ltcPts:
        resultPoints.append( cmds.xform(ltcPt,q=1,os=1,t=1) )

    # 
    ## ダミーと回避処理を無かったことにする
    cmds.delete(p_dmyLtc)
    cmds.connectAttr(reCnctNode[0],reCnctNode[1],f=1)

    # 
    ## setAttr(.cc)するための記述を纏める。mel版    
    vCmd = ' '.join( [str(sd),str(td),str(ud),str(sd*td*ud)] )
    strPts = []
    for pt in resultPoints:
        for value in pt:
            strPts.append(str(value))
    ptCmd = ' '.join(strPts)
    setMel = 'setAttr "%s.cc" -type lattice %s;'%(ltc,vCmd+' '+ptCmd)
    print setMel
    mel.eval(setMel)


    ## setAttr(.cc)するための記述を纏める。python版
    #    
    ## 記事では触れませんが、pythonはargumentを255個以上渡すとエラーになるようです。
    #
    #vCmd = ','.join( [str(sd),str(td),str(ud),str(sd*td*ud)] )
    #strPts = []
    #for pt in resultPoints:
    #    strPts.append(str(pt))
    #ptCmd = ','.join(strPts)
    #setCmd = 'cmds.setAttr("%s.cc",%s,type="lattice")'%(ltc,vCmd+','+ptCmd)
    #print setCmd
    #cmds.delete(p_dmyLtc)
    #eval(setCmd)

#
# LatticeBase,LatticeBaseShape,FFD のノードを取得する。
def getltcNodes(ltc):
    ffd_node = cmds.listConnections(ltc,s=0,d=1)
    ltc_base = cmds.ls(cmds.listConnections(ffd_node,s=1,d=0,sh=1),type='baseLattice')
    p_base = cmds.listRelatives(ltc_base[0],p=1)[0]
    return p_base,ltc_base[0],ffd_node[0]

#
# Latticeのデータ型を取得するためにダミーのLatticeを新しく作成する。
def createDmyLtc(node,sd,td,ud):
    p_ltc = cmds.createNode('transform',ss=1,n = 'dmy_'+node,p = node)
    d_ltc = cmds.createNode('lattice',ss=1,n = 'dmy_'+node+'Shape',p = p_ltc)
    cmds.setAttr(d_ltc+'.sDivisions',sd)
    cmds.setAttr(d_ltc+'.tDivisions',td)
    cmds.setAttr(d_ltc+'.uDivisions',ud)
    cmds.parent(p_ltc,w=1)
    return p_ltc,d_ltc

 
軽く処理の流れを説明します。

  1. Divisionを編集したいLatticeを指定
  2. 入力されたDivisionでダミーLatticeを作成する
  3. Latticeが動いている場合用にobject座標を取得するために小手先処理
  4. LatticeにダミーLatticeを追加
  5. 変形したダミーLatticeの頂点座標を取得する
  6. Lattice.cc用にsetAttr文を整理する
  7. Lattice.ccにset

見ての通り無理やり感がありますね…。
2、4、5、はFFD(free form deformation)の関数が用意されていればスマートに書けたのに…。
3、はmatrix関数を使えばコネクションを繋げ直したりしなくてもよくなります。
6、7、はlattice型をgetAttr出来ればいいのに…。

2、4、5、はFFDが実装出来たら記事がたくさん書けるのでパスします。と言うか荷が重いです^ ^
3、は行数が増えるので今回はパスします。
今回は特別に6、7、の処理をOpenMayaで書きました!!解説はしません!!w

import maya.cmds as cmds
import maya.api.OpenMaya as om

def editDivisions2(*arg,**kwargs):
    sd = kwargs.get('sDivisions',kwargs.get('s',2))
    td = kwargs.get('tDivisions',kwargs.get('t',2))
    ud = kwargs.get('uDivisions',kwargs.get('u',2))
    ltc = arg
    if not arg:
        ltc_list = cmds.ls(cmds.ls(sl=1,dag=1,s=1),type='lattice')
        if not ltc_list:
            cmds.warning('please select lattice node.')
            return
    else:
        ltc_list = cmds.ls(cmds.ls(ltc,dag=1,s=1),type='lattice')
        if not ltc_list:
            cmds.warning('please select lattice node.')
            return
    
    ltc = ltc_list[0]
    p_ltc = cmds.listRelatives(ltc,p=1)[0]
    p_base,ltc_base,ltc_ffd = getltcNodes(ltc)
    reCnctNode = cmds.listConnections(ltc_base+'.worldMatrix[0]',c=1,p=1)
    cmds.connectAttr(p_ltc+'.worldMatrix[0]',ltc_ffd+'.baseLatticeMatrix',f=1)
    p_dmyLtc,dmyLtc = createDmyLtc(p_ltc,sd,td,ud)
    cmds.lattice(ltc,e=1,g=dmyLtc)
    
    ltcData = getMData(dmyLtc,a='cc')
    setMData(ltc,a='cc',id=ltcData)
    
    cmds.delete(p_dmyLtc)
    cmds.connectAttr(reCnctNode[0],reCnctNode[1],f=1)

def getMObject(node):
    sel_list = om.MSelectionList()
    sel_list.add( node )
    dagPath = sel_list.getDagPath( 0 )
    dagPath.extendToShape()
    return dagPath.node()
    
def getMData(*arg,**kwargs):
    attr = kwargs.get('attr',kwargs.get('a',None))
    mobj = getMObject(arg[0])
    
    nodeFn = om.MFnDependencyNode( mobj )
    attribute = nodeFn.attribute(attr)
    plug = om.MPlug(mobj, attribute)
    return plug.asMObject()
    
def setMData(*arg,**kwargs):
    inData = kwargs.get('inData',kwargs.get('id',None))
    attr = kwargs.get('attr',kwargs.get('a',None))
    mobj = getMObject(arg[0])
 
    nodeFn = om.MFnDependencyNode( mobj )
    attribute = nodeFn.attribute(attr)
    inPlug  = om.MPlug(mobj, attribute)
    inPlug.setMObject(inData)

 

OpenMayaはステキですね!!scriptレベルでなんでも出来る!!
cachedいいですね。
latticeに注目していなかったら気が付かなかったなぁ…。
これはたぶんMeshでも出来そうな予感…。夢が広がる…!!

ちなみにundoは出来ませんので注意を!!w
それではまた~

※免責事項※
本記事内で公開している全ての手法・コードの有用性、安全性について、当方は一切の保証を与えるものではありません。
これらのコードを使用したことによって引き起こる直接的、間接的な損害に対し、当方は一切責任を負うものではありません。
自己責任でご使用ください。

Pocket

コメント

コメントはありません

コメントフォーム

コメントは承認制ですので、即時に反映されません。ご了承ください。

*