このトピックでは、Maya のシーン エレメント構成の基本について説明します。
Maya のシーン グラフは、通常は「無閉路有向グラフ」または DAG と呼ばれます。DAG は、実際には、シェーダ、デフォーマ、コンストレイントなど、非常に多様なノード タイプを含む、ディペンデンシー グラフ**と呼ばれるかなり大きなグラフのサブセットです。詳細については、次のセクション「ディペンデンシーグラフ プラグインの基本」を参照してください。ここでは、2 つの方法のいずれかで分類できる DAG ノードにフォーカスします。
MFn.kTransform
): このノード タイプは、下階層にあるすべてのオブジェクトに影響するローカルの 4x4 変換行列を定義します。この変換データは、MFnTransform
関数セットによって操作します。トランスフォーム ノードは、シーン エレメントをグループ化する目的で他のトランスフォーム ノードを子として持つことができます。MFn.kMesh
、MFn.kCamera
、MFn.kLight
... ): このノード タイプには、メッシュの頂点など、シーン エレメントの実際のジオメトリ情報が含まれます。シェイプ ノードは、常にトランスフォーム ノードを親として持ちます。次の図は、基本的なシーンの簡素化された無閉路有向グラフ(DAG)を表します。world
ノードは、シーンのルートを表します。緑色の円は、トランスフォーム ノード(kTransform
)に対応しており、シーン内のシェイプの位置決めが可能です。シェイプ ノードは、青色の円で示されます。この DAG では、perspShape
はシーン内のカメラ、pCubeShape1
はシーン内のキュービック メッシュ、pointLightShape1
はシーン内のポイント ライトをそれぞれ表します。pointLight1
トランスフォーム ノードは、pCube1
トランスフォーム ノードの子です。つまり、pCube1
トランスフォーム ノードが移動された場合には、ポイント ライトも移動します。
注: 通常、シェイプ ノードはその下に子を持つことはできません。この規則の例外は、underworld と呼ばれる特別な状況です。underworld は、そのルートがシェイプ ノードの子としてアタッチされる DAG サブグラフです。この underworld グラフは、NURBS カーブや NURBS サーフェスのコントロール ポイントおよび依存関係を定義します。
DAG 内の特定のシーン エレメントの位置は、MDagPath
オブジェクトによって指定されます。
MDagPath
は、MDagPath.node()
を使用して DAG で対応する MObject
を返すことができます。パス自体を拡張してシェイプ ノード(MDagPath.extendToShape()
)を含めるか、最も低いトランスフォーム ノード(MDagPath.transform()
)を返していくつかの便利な関数を指定することができます。MDagPath.fullPathName()
関数は、指定されたノードまでの DAG パスの文字列表現を返します。文字列表現は、DAG の名前のないルートから始まる、ノード名をパイプ(「|
」)で区切ったシーケンスとしての書式をとります。上の図では、ルート world
ノードから pointLightShape1
までのパスの文字列表現は、「|pCube1|pointLight1|pointLightShape1
」のようになります(ルート ノードに名前がないことに注目してください)。
メモリの消費を減らすために、密度が特別に高いメッシュなど、複雑なシェイプをシーン グラフの複数の場所でインスタンス化できます。つまり、コピーすることなく、同じシェイプがシェイプの複数の位置にくるようにできます。これを実現するために、DAG 内の複数のトランスフォーム ノードを同じシェイプ ノードの親にすることができます。そのようにして、1 つのシェイプ ノードに DAG のルートからの複数のパスを持たせることができます。インスタンス化されたシェイプ ノードを特定するために、MDagPath.isInstanced()
および MDagPath.instanceNumber()
などの関数を使用できます。
シーン グラフは、MItDag
オブジェクトを使用してトラバースできます。DAG はディペンデンシー グラフのサブセットであるため、必要に応じて MItDependencyGraph
オブジェクトを使用できます。
先頭に MIt
が追加されたクラスはイテレータと呼ばれ、コレクション内の各オブジェクトを検査する目的で使用できます。この例では、MItDag
によってシーン内の DAG ノードを反復できます。以下のサンプル コードでは、DAG イテレータを作成します。このイテレータが、ルートからシーンのトラバースを開始して、深さ優先で各ノードに移動します。OpenMaya.MFn.kInvalid
パラメータにより、MItDag
オブジェクトがどのノード タイプもフィルタしなくなります。
dagIterator = OpenMaya.MItDag( OpenMaya.MItDag.kDepthFirst, OpenMaya.MFn.kInvalid )
# This reference to the MFnDagNode function set will be needed
# to obtain information about the DAG objects.
dagNodeFn = OpenMaya.MFnDagNode()
MItDag.isDone()
関数により、検査すべきオブジェクトが残っているかどうかを判別します。MItDag.currentItem()
関数は、イテレータの現在の DAG オブジェクトを返します。この DAG オブジェクトのルートからの相対的な深度は、MItDag.depth()
を使用して取得できます。MItDag.next()
を呼び出すと、イテレータの内部状態が次の項目に進みますが、DAG オブジェクトは返しません。これを唯一達成できるのは、MItDag.currentItem()
を使用する方法です。これ以上反復を続ける項目がない場合には、MItDag.isDone()
関数によって True
が返されます。したがって、シーン グラフをトラバースする簡易な while ループを作成できます。
# Traverse the scene.
while( not dagIterator.isDone() ):
# Obtain the current item.
dagObject = dagIterator.currentItem()
# Extract the depth of the DAG object.
depth = dagIterator.depth()
# Make our MFnDagNode function set operate on the current DAG object.
dagNodeFn.setObject( dagObject )
# Extract the DAG object's name.
name = dagNodeFn.name()
print name + ' (' + dagObject.apiTypeStr() + ') depth: ' + str( depth )
# Iterate to the next item.
dagIterator.next()
注: 関数セット(例:
MFnDagNode
)を使用する方法については、「オブジェクトの作成と操作」を参照してください。
Maya のユーザ インタフェース(またはスクリプト)を使用してオブジェクトを選択すると、オブジェクトがグローバル アクティブ セレクション リストに追加され、MGlobal.getActiveSelectionList()
を使用してアクセスできるようになります。スタティック クラス MGlobal
は、Maya アプリケーション、ロギング、オブジェクト選択、コマンド実行、3D ビュー(シーンのアップ軸を含む)、およびモデル操作に関する各種の関数を含んでいます。
以下のコードは、MSelectionList
オブジェクトを投入することによって、アクティブ セレクション リストを取得する方法を例示しています。
Python API 2.0:
selectionList = OpenMaya.MGlobal.getActiveSelectionList()
Python API 1.0:
selectionList = OpenMaya.MSelectionList()
OpenMaya.MGlobal.getActiveSelectionList( selectionList )
この例では、MItSelectionList
のインスタンスを使用してセレクション リストを反復します。MItSelectionList
のコンストラクタを使用してフィルタを指定することにより、特定のタイプのオブジェクトを反復できます。以下の例では、MFn.kDagNode
パラメータを指定します。それにより、この例のイテレータは、MFnDagNode
関数と互換性があるオブジェクトに対してフィルタを実行します。
iterator = OpenMaya.MItSelectionList( selectionList, OpenMaya.MFn.kDagNode )
Python API 2.0 で、選択リストを使用して、シーン内のすべてのノードを最初に選択し、各項目のイテレータに getDagPath()
を呼び出すことによって、シーン グラフ全体を繰り返し処理することができます。たとえば、次のようになります。
dagNodeFn = OpenMaya.MFnDagNode()
cmds.select(all=True)
selectionList = OpenMaya.MGlobal.getActiveSelectionList()
if sList.length()>0:
iterator = OpenMaya.MItSelectionList(selectionList, OpenMaya.MFn.kDagNode)
while not iterator.isDone():
print iterator.getDagPath()
iterator.next()
ファイル名: printPaths.py
スクリプト エディタ出力の例:
プログラムの概要: 以下のプラグイン コードによって、printPaths()
コマンドが作成されます。このコマンドの動作は、アクティブ セレクション リスト に DAG オブジェクトが含まれるかどうかに応じて変わります。1 つまたは複数の DAG オブジェクトが選択されている場合、各オブジェクトの名前、タイプ、DAG パス、および互換性のある関数セット タイプがスクリプト エディタに出力されます。そうでない場合には、DAG ノードの名前およびタイプを使用して、シーン グラフが出力されます。
Python API 2.0:
# pyPrintPaths.py
import sys
import maya.cmds as cmds
import maya.api.OpenMaya as OpenMaya
def maya_useNewAPI():
"""
The presence of this function tells Maya that the plugin produces, and
expects to be passed, objects created using the Maya Python API 2.0.
"""
pass
kPluginCmdName = 'pyPrintPaths'
##########################################################
# Plug-in
##########################################################
class printPathsCmd(OpenMaya.MPxCommand):
def __init__(self):
''' Constructor. '''
OpenMaya.MPxCommand.__init__(self)
def doIt(self, args):
'''
Print the DAG paths of the selected objects.
If no DAG objects are selected, print the entire
scene graph.
'''
# Populate the MSelectionList with the currently selected
# objects using the static function MGlobal.getActiveSelectionList().
#selectionList = OpenMaya.MSelectionList()
selectionList = OpenMaya.MGlobal.getActiveSelectionList()
# This selection list can contain more than just scene elements (DAG nodes),
# so we must create an iterator over this selection list (MItSelectionList),
# and filter for objects compatible with the MFnDagNode function set (MFn.kDagNode).
iterator = OpenMaya.MItSelectionList( selectionList, OpenMaya.MFn.kDagNode )
if iterator.isDone():
# Print the whole scene if there are no DAG nodes selected.
print '====================='
print ' SCENE GRAPH (DAG): '
print '====================='
self.printScene()
else:
# Print the paths of the selected DAG objects.
print '======================='
print ' SELECTED DAG OBJECTS: '
print '======================='
self.printSelectedDAGPaths( iterator )
def printSelectedDAGPaths(self, pSelectionListIterator):
''' Print the DAG path(s) of the selected object(s). '''
# Create an MDagPath object which will be populated on each iteration.
dagPath = OpenMaya.MDagPath()
# Obtain a reference to MFnDag function set to print the name of the DAG object
dagFn = OpenMaya.MFnDagNode()
# Perform each iteration.
while( not pSelectionListIterator.isDone() ):
# Populate our MDagPath object. This will likely provide
# us with a Transform node.
dagPath = pSelectionListIterator.getDagPath()
try:
# Attempt to extend the path to the shape node.
dagPath.extendToShape()
except Exception as e:
# Do nothing if this operation fails.
pass
# Obtain the name of the object.
dagObject = dagPath.node()
dagFn.setObject( dagObject )
name = dagFn.name()
# Obtain the compatible function sets for this DAG object.
# These values refer to the enumeration values of MFn
fntypes = []
fntypes = OpenMaya.MGlobal.getFunctionSetList( dagObject )
# Print the DAG object information.
print name + ' (' + dagObject.apiTypeStr + ')'
print '¥tDAG path: [' + str( dagPath.fullPathName() ) + ']'
print '¥tCompatible function sets: ' + str( fntypes )
# Advance to the next item
pSelectionListIterator.next()
print '====================='
def printScene(self):
''' Traverse and print the elements in the scene graph (DAG) '''
# Create a function set which we will re-use throughout our scene graph traversal.
dagNodeFn = OpenMaya.MFnDagNode()
# Create an iterator to traverse the scene graph starting at the world node
# (the scene's origin). We use a depth-first traversal, and we do not filter for
# any scene elements, as indicated by the 'OpenMaya.MFn.kInvalid' parameter.
dagIterator = OpenMaya.MItDag( OpenMaya.MItDag.kDepthFirst,
OpenMaya.MFn.kInvalid )
# Traverse the scene.
while( not dagIterator.isDone() ):
# Obtain the current item.
dagObject = dagIterator.currentItem()
depth = dagIterator.depth()
# Make our MFnDagNode function set operate on the current DAG object.
dagNodeFn.setObject( dagObject )
# Extract the DAG object's name.
name = dagNodeFn.name()
# Generate our output by first incrementing the tabs based on the depth
# of the current object. This formats our output nicely.
output = ''
for i in range( 0, depth ):
output += '¥t'
output += name + ' (' + dagObject.apiTypeStr + ')'
print output
# Increment to the next item.
dagIterator.next()
print '====================='
##########################################################
# Plug-in initialization.
##########################################################
def cmdCreator():
''' Creates an instance of our command class. '''
return printPathsCmd()
def initializePlugin(mobject):
''' Initializes the plug-in.'''
mplugin = OpenMaya.MFnPlugin( mobject )
try:
mplugin.registerCommand( kPluginCmdName, cmdCreator )
except:
sys.stderr.write( "Failed to register command: %s¥n" % kPluginCmdName )
def uninitializePlugin(mobject):
''' Uninitializes the plug-in '''
mplugin = OpenMaya.MFnPlugin( mobject )
try:
mplugin.deregisterCommand( kPluginCmdName )
except:
sys.stderr.write( "Failed to unregister command: %s¥n" % kPluginCmdName )
##########################################################
# Sample usage.
##########################################################
'''
# Copy the following lines and run them in Maya's Python Script Editor:
import maya.cmds as cmds
cmds.loadPlugin( 'pyPrintPaths.py' )
cmds.pyPrintPaths()
'''
Python API 1.0:
# printPaths.py
import sys
import maya.OpenMayaMPx as OpenMayaMPx
import maya.OpenMaya as OpenMaya
kPluginCmdName = 'printPaths'
##########################################################
# Plug-in
##########################################################
class printPathsCmd(OpenMayaMPx.MPxCommand):
def __init__(self):
''' Constructor. '''
OpenMayaMPx.MPxCommand.__init__(self)
def doIt(self, args):
'''
Print the DAG paths of the selected objects.
If no DAG objects are selected, print the entire
scene graph.
'''
# Populate the MSelectionList with the currently selected
# objects using the static function MGlobal.getActiveSelectionList().
selectionList = OpenMaya.MSelectionList()
OpenMaya.MGlobal.getActiveSelectionList( selectionList )
# This selection list can contain more than just scene elements (DAG nodes),
# so we must create an iterator over this selection list (MItSelectionList),
# and filter for objects compatible with the MFnDagNode function set (MFn.kDagNode).
iterator = OpenMaya.MItSelectionList( selectionList, OpenMaya.MFn.kDagNode )
if iterator.isDone():
# Print the whole scene if there are no DAG nodes selected.
self.printScene()
else:
# Print the paths of the selected DAG objects.
self.printSelectedDAGPaths( iterator )
def printSelectedDAGPaths(self, pSelectionListIterator):
''' Print the DAG path(s) of the selected object(s). '''
# Create an MDagPath object which will be populated on each iteration.
dagPath = OpenMaya.MDagPath()
# Obtain a reference to MFnDag function set to print the name of the DAG object
dagFn = OpenMaya.MFnDagNode()
print '======================='
print ' SELECTED DAG OBJECTS: '
print '======================='
# Perform each iteration.
while( not pSelectionListIterator.isDone() ):
# Populate our MDagPath object. This will likely provide
# us with a Transform node.
pSelectionListIterator.getDagPath( dagPath )
try:
# Attempt to extend the path to the shape node.
dagPath.extendToShape()
except Exception as e:
# Do nothing if this operation fails.
pass
# Obtain the name of the object.
dagObject = dagPath.node()
dagFn.setObject( dagObject )
name = dagFn.name()
# Obtain the compatible function sets for this DAG object.
# These values refer to the enumeration values of MFn
fntypes = []
OpenMaya.MGlobal.getFunctionSetList( dagObject, fntypes )
# Print the DAG object information.
print name + ' (' + dagObject.apiTypeStr() + ')'
print '¥tDAG path: [' + str( dagPath.fullPathName() ) + ']'
print '¥tCompatible function sets: ' + str( fntypes )
# Advance to the next item
pSelectionListIterator.next()
print '====================='
def printScene(self):
''' Traverse and print the elements in the scene graph (DAG) '''
# Create a function set which we will re-use throughout our scene graph traversal.
dagNodeFn = OpenMaya.MFnDagNode()
# Create an iterator to traverse the scene graph starting at the world node
# (the scene's origin). We use a depth-first traversal, and we do not filter for
# any scene elements, as indicated by the 'OpenMaya.MFn.kInvalid' parameter.
dagIterator = OpenMaya.MItDag( OpenMaya.MItDag.kDepthFirst,
OpenMaya.MFn.kInvalid )
print '====================='
print ' SCENE GRAPH (DAG): '
print '====================='
# Traverse the scene.
while( not dagIterator.isDone() ):
# Obtain the current item.
dagObject = dagIterator.currentItem()
depth = dagIterator.depth()
# Make our MFnDagNode function set operate on the current DAG object.
dagNodeFn.setObject( dagObject )
# Extract the DAG object's name.
name = dagNodeFn.name()
# Generate our output by first incrementing the tabs based on the depth
# of the current object. This formats our output nicely.
output = ''
for i in range( 0, depth ):
output += '¥t'
output += name + ' (' + dagObject.apiTypeStr() + ')'
print output
# Increment to the next item.
dagIterator.next()
print '====================='
##########################################################
# Plug-in initialization.
##########################################################
def cmdCreator():
''' Creates an instance of our command class. '''
return OpenMayaMPx.asMPxPtr( printPathsCmd() )
def initializePlugin(mobject):
''' Initializes the plug-in.'''
mplugin = OpenMayaMPx.MFnPlugin( mobject )
try:
mplugin.registerCommand( kPluginCmdName, cmdCreator )
except:
sys.stderr.write( "Failed to register command: %s¥n" % kPluginCmdName )
def uninitializePlugin(mobject):
''' Uninitializes the plug-in '''
mplugin = OpenMayaMPx.MFnPlugin( mobject )
try:
mplugin.deregisterCommand( kPluginCmdName )
except:
sys.stderr.write( "Failed to unregister command: %s¥n" % kPluginCmdName )
##########################################################
# Sample usage.
##########################################################
'''
# Copy the following lines and run them in Maya's Python Script Editor:
import maya.cmds as cmds
cmds.loadPlugin( 'printPaths.py' )
cmds.printPaths()
'''