ポリゴン変更プラグイン

メッシュの変更

Maya で mesh のデーターを入力し、何らかの処理をして出力するノードを作成するとき、 すべての頂点・エッジ・フェース・UV などに変更を加えるには、 maya.OpenMaya クラスのイテレータクラスのメソッドを使用します。

MItGeometry
ジオメトリデータのイテレータ
MItMeshEdge
ポリゴンエッジのイテレータ
MItMeshFaceVertex
ポリゴンフェースのイテレータ
MItMeshVertex
ポリゴン頂点のイテレータ
MItMeshPolygon
ポリゴンのイテレータ

イテレータ

イテレータとは、繰り返し処理を抽象化したものです。
イテレータを使うことによって、要素数を気にすることがなくなり、 スクリプトが簡単になります。
MEL における for( in ) もイテレータです。
maya.OpenMaya に属するクラスのイテレータは、 isDone()next() メソッドを持ちます。

meshIter = maya.OpenMaya.MItメソッド名()
while not meshIter.isDone():
    # ここにメッシュに関する処理を書きます
    meshIter.next()

メッシュ変更プラグイン

ポリゴンメッシュを読み込んで、頂点をランダムに移動するプラグインを作ってみましょう。

  1. 以下の Python スクリプトを randomPoly1.py という名前で作ります。
    import sys
    import random
    import maya.OpenMaya
    import maya.OpenMayaMPx
    
    kPluginNodeName = 'randomPoly1'
    kPluginNodeId = maya.OpenMaya.MTypeId(0x87008)
    
    class randomPoly1(maya.OpenMayaMPx.MPxNode):
        rangeX = maya.OpenMaya.MObject()
        rangeY = maya.OpenMaya.MObject()
        inputMesh = maya.OpenMaya.MObject()
        outputMesh = maya.OpenMaya.MObject()
    
        def __init__(self):
            maya.OpenMayaMPx.MPxNode.__init__(self)
    
        def _randomMesh(self, rx, ry, outData):
            vertexIter = maya.OpenMaya.MItMeshVertex(outData)
            while not vertexIter.isDone():
                v = maya.OpenMaya.MVector()
                v.x = random.uniform(rx, ry)
                v.y = random.uniform(rx, ry)
                v.z = random.uniform(rx, ry)
                vertexIter.translateBy(v, maya.OpenMaya.MSpace.kWorld)
                vertexIter.next()
    
        def compute(self, plug, data):
            if plug == randomPoly1.outputMesh:
                dataHandle = data.inputValue(randomPoly1.rangeX)
                rx = dataHandle.asFloat()
                dataHandle = data.inputValue(randomPoly1.rangeY)
                ry = dataHandle.asFloat()
    
                dataHandle = data.inputValue(randomPoly1.inputMesh)
    
                outputHandle = data.outputValue(randomPoly1.outputMesh)
                outputHandle.setMObject(dataHandle.asMesh())
                self._randomMesh(rx, ry, outputHandle.asMesh())
    
                data.setClean(plug)
            else:
                return maya.OpenMaya.kUnknownParameter
    
    def nodeCreator():
        return maya.OpenMayaMPx.asMPxPtr(randomPoly1())
    
    def nodeInitializer():
        nAttr = maya.OpenMaya.MFnNumericAttribute()
        randomPoly1.rangeX = nAttr.create('rangeX', 'rx', maya.OpenMaya.MFnNumericData.kFloat, 0.0)
        nAttr.setStorable(True)
    
        randomPoly1.rangeY = nAttr.create('rangeY', 'ry', maya.OpenMaya.MFnNumericData.kFloat, 1.0)
        nAttr.setStorable(True)
    
        typedAttr = maya.OpenMaya.MFnTypedAttribute()
        randomPoly1.inputMesh = typedAttr.create('inputMesh', 'im', maya.OpenMaya.MFnData.kMesh)
        typedAttr.setStorable(True)
    
        randomPoly1.outputMesh = typedAttr.create('outputMesh', 'om', maya.OpenMaya.MFnData.kMesh)
        typedAttr.setStorable(False)
        typedAttr.setWritable(False)
    
        randomPoly1.addAttribute(randomPoly1.rangeX)
        randomPoly1.addAttribute(randomPoly1.rangeY)
        randomPoly1.addAttribute(randomPoly1.inputMesh)
        randomPoly1.addAttribute(randomPoly1.outputMesh)
    
        randomPoly1.attributeAffects(randomPoly1.rangeX, randomPoly1.outputMesh)
        randomPoly1.attributeAffects(randomPoly1.rangeY, randomPoly1.outputMesh)
        randomPoly1.attributeAffects(randomPoly1.inputMesh, randomPoly1.outputMesh)
    
    
    def initializePlugin(mobject):
        mplugin = maya.OpenMayaMPx.MFnPlugin(mobject)
        try:
            mplugin.registerNode(kPluginNodeName, kPluginNodeId, nodeCreator, nodeInitializer)
        except:
            sys.stderr.write('Failed to register node: %s' % kPluginNodeName)
            raise
    
    def uninitializePlugin(mobject):
        mplugin = maya.OpenMayaMPx.MFnPlugin(mobject)
        try:
            mplugin.deregisterNode( kPluginNodeId )
        except:
            sys.stderr.write('Failed to deregister node: %s' % kPluginNodeName)
            raise
    
  2. randomPoly1.py をロードします。
    以下のどちらかの方法でロードします。
  3. スクリプト エディタ のインプットウインドウで以下のように実行してみます。
    import maya.cmds
    obj = maya.cmds.polyPlane(w=10, h=10)
    dattr = maya.cmds.listConnections(obj[0] + '.i', p=True)
    maya.cmds.disconnectAttr(dattr[0], obj[0] + '.i')
    dc = maya.cmds.createNode('randomPoly1')
    maya.cmds.connectAttr(dattr[0], dc + '.im')
    maya.cmds.connectAttr(dc + '.om', obj[0] + '.i')
    

スクリプトの解説

import sys
sys モジュールをインポートします。
エラーの表示をするために必要です。
import random
random モジュールをインポートします。
uniform() 関数を使うために必要です。
import maya.OpenMaya
maya.OpenMaya モジュールをインポートします。
import maya.OpenMayaMPx
maya.OpenMayaMPx モジュールをインポートします。
kPluginNodeName = 'randomPoly1'
このプラグインの名前を simplePoly1 として、変数 kPluginNodeName に代入します。
kPluginNodeId = maya.OpenMaya.MTypeId(0x87008)
プラグインごとにユニークな識別番号を決めます。
プラグインは、この番号によって Maya の中で登録・識別・登録取り消しなどが行われます。
ローカルだけで使用する場合などは0x00000000以上、0x0007ffff以下の番号を、 プラグインを公表する場合は Autodesk からユニークな番号を取得します。
class randomPoly1(maya.OpenMayaMPx.MPxNode):
maya.OpenMayaMPx.MPxNode を継承して、 ノード変更のためのクラスを定義します。
rangeX = maya.OpenMaya.MObject()
入力データのために、MObject インスタンスを作成して、 クラス変数 rangeX に代入します。
rangeY = maya.OpenMaya.MObject()
入力データのために、MObject インスタンスを作成して、 クラス変数 rangeY に代入します。
inputMesh = maya.OpenMaya.MObject()
入力データのために、MObject インスタンスを作成して、 クラス変数 inputMesh に代入します。
この inputMesh はプレーンの mesh データが入るアトリビュートです。
outputMesh = maya.OpenMaya.MObject()
出力データのために、MObject インスタンスを作成して、 クラス変数 outputMesh に代入します。
この outputMesh はプレーンの mesh データが入るアトリビュートです。
def __init__(self):
randomPoly1 クラス初期化のためのメソッドです。
maya.OpenMayaMPx.MPxNode.__init__(self)
sineNode1 クラスを初期化するために、 親クラス MPxNode の __init__() を実行します。
def _randomMesh(self, rx, ry, outData):
メッシュの頂点をランダムに移動するメソッド _randomMesh() を定義します。
引数の rxry は 移動する範囲を表します。
vertexIter = maya.OpenMaya.MItMeshVertex(outData)
MItMeshVertex() によって outData から頂点データのイテレータを得ます
while not vertexIter.isDone():
イテレータのデータが終わるまで while によるループを実行します。
v = maya.OpenMaya.MVector()
頂点データを移動する距離を代入しておくための MVector 型のオブジェクトを作ります。
v.x = random.uniform(rx, ry)
v.y = random.uniform(rx, ry)
v.z = random.uniform(rx, ry)
uniform() によって rx から ry までの間の乱数を MVector の v.x, v.y, v.z に代入します。
vertexIter.translateBy(v, maya.OpenMaya.MSpace.kWorld)
outData の頂点データをワールドスペース座標で、 元の位置から v だけ移動します。
kWorld を kObject に変更するとローカル座標での移動になります。
vertexIter.next()
イテレータ vertexIter に次の頂点を設定します。
def compute(self, plug, data):
ノードが実行する処理を記述しておく関数です。
引数の意味は以下のとおりです。
plug
他のノードにコネクトされているプラグを表します。
data
このノードに関するデータを保存しているデータブロックオブジェクトです。
if plug == randomPoly1.outputMesh:
プラグが出力 outputMesh かどうか調べます。
dataHandle = data.inputValue(randomPoly1.rangeX)
データブロックから、入力データ rangeX のデータハンドルを得て、 dataHandle に代入します。
rx = dataHandle.asFloat()
入力データをデータハンドルから float のデータとして取り出し、 rx に代入します。
dataHandle = data.inputValue(randomPoly1.rangeY)
データブロックから、入力データ rangeY のデータハンドルを得て、 dataHandle に代入します。
ry = dataHandle.asFloat()
入力データをデータハンドルから float のデータとして取り出し、 ry に代入します。
dataHandle = data.inputValue(randomPoly1.inputMesh)
データブロックから、入力データ inputMesh のデータハンドルを得て、 dataHandle に代入します。
outputHandle = data.outputValue(randomPoly1.outputMesh)
データブロックから、出力データ outputMesh のデータハンドルを得て、 outputHandle に代入します。
outputHandle.setMObject(dataHandle.asMesh())
出力データとして、入力データハンドルの mesh データを設定します。
self._randomMesh(rx, ry, outputHandle.asMesh())
出力データを mesh として、メソッド _randomMesh() に渡して頂点をランダムに移動します。
data.setClean(plug)
プラグをクリーンにします。 これをしないとノードの計算が終わっているとみなされません。
else:
  return maya.OpenMaya.kUnknownParameter
プラグが outputMesh 以外の場合は、kUnknownParameter を返して Maya に処理を任せます。
計算する必要の無いプラグは、kUnknownParameter を返しておきます。
def nodeCreator():
Maya がノードのインスタンスを作成するための関数です。
return maya.OpenMayaMPx.asMPxPtr(randomPoly1())
simplePoly1 クラスのインスタンスを作成し、asMPxPtr() 関数に与えます。
asMPxPtr() 関数を使わないとインスタンスを Maya に渡すことができません。
def nodeInitializer():
ノードの初期化関数です。
このノードが作成されるときに実行されます。
nAttr = maya.OpenMaya.MFnNumericAttribute()
MFnNumericAttribute のインスタンスを作成します。
数値型のアトリビュートを作成するのに使用されます。
randomPoly1.rangeX = nAttr.create('rangeX', 'rx', maya.OpenMaya.MFnNumericData.kFloat, 0.0)
rangeX アトリビュートを作成し、短縮名は rx 、型は float 、デフォールト値は 0.0 にします。
nAttr.setStorable(True)
シーンの保存時に rangeX アトリビュートの値を保存する設定です。
randomPoly1.rangeY = nAttr.create('rangeY', 'ry', maya.OpenMaya.MFnNumericData.kFloat, 1.0)
rangeY アトリビュートを作成し、短縮名は ry 、型は float 、デフォールト値は 1.0 にします。
nAttr.setStorable(True)
シーンの保存時に rangeY アトリビュートの値を保存する設定です。
typedAttr = maya.OpenMaya.MFnTypedAttribute()
MFnTypedAttribute のインスタンスを作成します。
mesh やコンポーネントなどのアトリビュートを作成するのに使用されます。
randomPoly1.inputMesh = typedAttr.create('inputMesh', 'im', maya.OpenMaya.MFnData.kMesh)
inputMesh アトリビュートを作成し、短縮名は im 、型は mesh にします。
typedAttr.setStorable(True)
シーンの保存時に inputMesh アトリビュートの値を保存する設定です。
randomPoly1.outputMesh = typedAttr.create('outputMesh', 'om', maya.OpenMaya.MFnData.kMesh)
outputMesh アトリビュートを作成し、短縮名は om 、型は mesh にします。
typedAttr.setStorable(False)
シーンの保存時に outputMesh アトリビュートの値を保存しない設定です。
typedAttr.setWritable(False)
outputMesh アトリビュートの値を書き込み不可にする設定です。
randomPoly1.addAttribute(randomPoly1.rangeX)
randomPoly1.addAttribute(randomPoly1.rangeY)
randomPoly1.addAttribute(randomPoly1.inputMesh)
randomPoly1.addAttribute(randomPoly1.outputMesh)
rangeX, rangeY, inputMesh, outputMesh の各アトリビュートを randomPoly1 のアトリビュートとして登録します。
randomPoly1.attributeAffects(randomPoly1.rangeX, randomPoly1.outputMesh)
rangeX アトリビュートが変化したとき、 プラグを outputMesh として randomPoly1 の compute メソッドを実行するように設定します。
randomPoly1.attributeAffects(randomPoly1.rangeY, randomPoly1.outputMesh)
rangeY アトリビュートが変化したとき、 プラグを outputMesh として randomPoly1 の compute メソッドを実行するように設定します。
randomPoly1.attributeAffects(randomPoly1.inputMesh, randomPoly1.outputMesh)
inputMesh アトリビュートが変化したとき、 プラグを outputMesh として randomPoly1 の compute メソッドを実行するように設定します。
def initializePlugin(mobject):
プラグインがロードされたときに実行される初期化関数です。
mplugin = maya.OpenMayaMPx.MFnPlugin(mobject)
MFnPlugin クラスのインスタンスを作成します。
mobject は MObject のインスタンスです。
try:
  mplugin.registerNode(kPluginNodeName, kPluginNodeId, nodeCreator, nodeInitializer)
except:
  sys.stderr.write('Failed to register node: %s' % kPluginNodeName)
  raise
MFnPlugin のインスタンスに、simplePoly1 に関する設定を与えています。
もし設定に失敗した場合は、メッセージを出力して例外を送出します。
def uninitializePlugin(mobject):
プラグインをアンロードするときに実行される関数です。
mplugin = maya.OpenMayaMPx.MFnPlugin(mobject)
MFnPlugin クラスのインスタンスを作成します。
mobject は MObject のインスタンスです。
try:
  mplugin.deregisterNode(kPluginNodeId)
except:
  sys.stderr.write('Failed to deregister node: %s' % kPluginNodeName)
  raise
プラグインをアンロードします。
もしアンロードに失敗した場合は、メッセージを出力して例外を送出します。

練習

  1. 上の randomPoly1.py を参考にして randomPoly2.py という Python プラグインを作り、 アトリビュート rangeX, rangeY の代わりに、 rangeX1, rangeX2, rangeY1, rangeY2, rangeZ1, rangeZ2 というアトリビュートを追加してください。
    rangeX1 (rx1)
    X 方向乱数値の最小値 (デフォールト値 0.0)
    rangeX2 (rx2)
    X 方向乱数値の最大値 (デフォールト値 1.0)
    rangeY1 (ry1)
    Y 方向乱数値の最小値 (デフォールト値 0.0)
    rangeY2 (ry2)
    Y 方向乱数値の最大値 (デフォールト値 1.0)
    rangeZ1 (rz1)
    Z 方向乱数値の最小値 (デフォールト値 0.0)
    rangeZ2 (rz2)
    Z 方向乱数値の最大値 (デフォールト値 1.0)
    (作成例)
    randomPoly2.py をプラグインマネージャなどで読み込んだ後、 以下の関数を実行します。
    import maya.cmds
    obj = maya.cmds.polyPlane(w=10, h=10)
    dattr = maya.cmds.listConnections(obj[0] + '.i', p=True)
    maya.cmds.disconnectAttr(dattr[0], obj[0] + '.i')
    dc = maya.cmds.createNode('randomPoly2')
    maya.cmds.connectAttr(dattr[0], dc + '.im')
    maya.cmds.connectAttr(dc + '.om', obj[0] + '.i')
    maya.cmds.setAttr(dc + '.rx1', -0.5)
    maya.cmds.setAttr(dc + '.rx2', 0.5)
    maya.cmds.setAttr(dc + '.ry1', 0.0)
    maya.cmds.setAttr(dc + '.ry2', 0.5)
    maya.cmds.setAttr(dc + '.rz1', -0.5)
    maya.cmds.setAttr(dc + '.rz2', 0.0)
    

参考


Prev
Home | Contents
Mail