記事投稿の頻度が少ないせいで、いつも記事の書き始めに困ります。一か月ぶりのくせして唐突に天気の話は無いし同じ理由で時事から始めるのもね。かといっていきなり本題から切り出すのも冷たい感じがします。
日を置かずに書くのが一番なんですけどね。
それでは本題、

Mayaのノードタイプの階層いわゆる上の図のような継承関係を知るには以下の2つがあります。(スクリプトはMaya2017で検証したものです)
一つ目、下は「mesh」ノードの継承元を調査します。
import pymel.core as pm
print pm.nodeType('mesh', inherited=True, isTypeName=True)
結果は、
[u'containerBase', u'entity', u'dagNode', u'shape', u'geometryShape', u'deformableShape', u'controlPoint', u'surfaceShape', u'mesh']
「containerBase」ノードをルートとして機能やアトリビュートを継承していることが分かります。(ホントのルートは「node」というノードタイプのはずなのですが、何故かここでは得られません)
二つ目は逆、「deformableShape」ノードを継承しているノードを調査します。
import pymel.core as pm pm.nodeType( typeName, derived=True, isTypeName=True )
結果は(長いので適当に改行します)
[u'nParticle', u'nRigid', u'nCloth', u'nBase', u'particle', u'motionTrailShape', u'snapshotShape', u'bezierCurve', u'nurbsCurve', u'curveShape', u'gpuCache', u'aiVolume', u'aiStandIn', u'VRayMetaball', u'VRayFurPreview', u'VRayVolumeGrid', u'VRayPlane', u'VRayMeshPreview', u'VRayClipperShape', u'xgmSplineDescription', u'xgmSplineGuide', u'xgmSphereGuide', u'xgmCardGuide', u'xgmArchiveGuide', u'xgmSubdPatch', u'xgmNurbsPatch', u'xgmSplineGuide', u'xgmSphereGuide', u'xgmCardGuide', u'xgmArchiveGuide', u'xgmGuide', u'xgmSubdPatch', u'xgmNurbsPatch', u'xgmPatch', u'xgmDescription', u'bifrostShape', u'THsurfaceShape', u'fluidTexture2D', u'fluidTexture3D', u'fluidShape', u'subdiv', u'nurbsSurface', u'greasePlaneRenderShape', u'mesh', u'heightField', u'surfaceShape', u'lattice', u'controlPoint', u'deformableShape']
2つ組み合わせると、例えば「deformableShape」を継承するノードタイプの継承元をすべて知る事が出来ます。
import pymel.core as pm
nodetypes = pm.nodeType( 'deformableShape', derived=True, isTypeName=True )
for nodetype in nodetypes:
print pm.nodeType( nodetype, inherited=True, isTypeName=True )
結果は(前半略)、
[u'containerBase', u'entity', u'dagNode', u'shape', u'geometryShape', u'deformableShape', u'controlPoint', u'surfaceShape', u'subdiv'] [u'containerBase', u'entity', u'dagNode', u'shape', u'geometryShape', u'deformableShape', u'controlPoint', u'surfaceShape', u'nurbsSurface'] [u'containerBase', u'entity', u'dagNode', u'shape', u'geometryShape', u'deformableShape', u'controlPoint', u'surfaceShape', u'mesh', u'greasePlaneRenderShape'] [u'containerBase', u'entity', u'dagNode', u'shape', u'geometryShape', u'deformableShape', u'controlPoint', u'surfaceShape', u'mesh'] [u'containerBase', u'entity', u'dagNode', u'shape', u'geometryShape', u'deformableShape', u'controlPoint', u'surfaceShape', u'heightField'] [u'containerBase', u'entity', u'dagNode', u'shape', u'geometryShape', u'deformableShape', u'controlPoint', u'surfaceShape'] [u'containerBase', u'entity', u'dagNode', u'shape', u'geometryShape', u'deformableShape', u'controlPoint', u'lattice'] [u'containerBase', u'entity', u'dagNode', u'shape', u'geometryShape', u'deformableShape', u'controlPoint'] [u'containerBase', u'entity', u'dagNode', u'shape', u'geometryShape', u'deformableShape']
のようになります。ひとくちに「deformableShape」ノードと言っても上のように継承したノードタイプがたくさんあることが分かります。
さてここからが本題、上記をもってある程度継承関係を知ることはできますがこれを木構造(階層)構造で取得したい場合は残念ながらコマンド一発でとはいきません。
目的は例えばPySideのQtGui.QTreeViewで一覧にしたい時などに使用できるかなと、、、実際にはずいぶん前にそう使いたいと思って書いたコードで、放置されていたものを今回紹介しています。
以下が、「shape」ノードをルートとして継承関係を木構造で取得するスクリプトです。ScriptEditorへペーストして実行できます。
import pymel.core as pm
import json
class NodeTypeTree(object):
def __init__(self,typeName):
super(NodeTypeTree, self).__init__()
self.root = None
self.nodeTypeRelationInfoList = []
#継承しているノードタイプ全てを取得
typeList = pm.nodeType( typeName, derived=True, isTypeName=True )
#基点のノードタイプのindex
shapeTypeDepth = len(pm.nodeType(typeName, inherited=True, isTypeName=True)) - 1
for type in typeList:
#基点から上のノードタイプを除外した継承元リスト
inheritList = (pm.nodeType(type, inherited=True, isTypeName=True))[shapeTypeDepth:]
n = inheritList[-1] #リストの最後が自身
if len([x for x in self.nodeTypeRelationInfoList if x.typeName == n]) != 0:
continue
p = None
if len(inheritList) > 1:
p = inheritList[-2] #リストの最後から2番目がひとつ上の継承元
self.nodeTypeRelationInfoList.append(NodeTypeRelationInfo(n,p))
#NodeTypeRelationInfoインスタンス全てを精査して継承元のchildrenへ追加
for n in self.nodeTypeRelationInfoList:
if n.parentTypeName != None:
[f.appendChild(n) for f in self.nodeTypeRelationInfoList if f.typeName == n.parentTypeName]
else:
#parentTypeNameがNoneならそれは基点
self.root = n
def getHierarchy(self):
return self.root.getHierarchy()
class NodeTypeRelationInfo(object):
def __init__(self,typeName,parentTypeName):
super(NodeTypeRelationInfo, self).__init__()
self.typeName = typeName #ノードタイプ名
self.parentTypeName = parentTypeName #継承元の名前
self.children = []
def appendChild(self,nodeTypeRelationInfo):
self.children.append(nodeTypeRelationInfo)
def getHierarchy(self):
#再帰的に子ノードタイプを取得
r = [c.getHierarchy() for c in self.children]
return {"typeName":self, "children":r}
def __str__(self):
return str(self.typeName)
#NodeTypeRelationInfoをJSONへシリアライズするためのエンコーダ
class NodeTypeRelationInfoEncoder(json.JSONEncoder):
def default(self,obj):
if isinstance(obj, NodeTypeRelationInfo):
return obj.typeName
return json.JSONEncoder.default(self, obj)
if __name__ == '__main__':
ntTree = NodeTypeTree('shape')
print json.dumps(ntTree.getHierarchy(),indent=4,cls=NodeTypeRelationInfoEncoder)
木構造の最小単位が「NodeTypeRelationInfo」クラスです、ノードタイプ名(typeName)とその一つ上の継承元のノードタイプ名(parentTypeName)を持ちchildrenには継承している「NodeTypeRelationInfo」インスタンスをリストとして持ちます。
木構造はクラス「NodeTypeTree」です。インスタンスはノードタイプ名で初期化します。rootに基点(根っこ)となる「NodeTypeRelationInfo」インスタンスが入ります。初期化時にまず全てのノードタイプに関して「NodeTypeRelationInfo」インスタンスを作成し、その後NodeTypeRelationInfoのparentTypeNameを精査してchildrenへ追加して行きます。最終的にrootを基点とした木構造が出来上がります。
結果は大きく省略しますが以下のようになります。
{
"typeName": "shape",
"children": [
{
"typeName": "softModHandle",
"children": []
},
{
"typeName": "flexorShape",
"children": [
{
"typeName": "clusterFlexorShape",
"children": []
}
]
},
{
"typeName": "deformFunc",
"children": [
{
"typeName": "deformWave",
"children": []
},
{
"typeName": "deformTwist",
"children": []
},
{
"typeName": "deformSquash",
"children": []
},
{
"typeName": "deformSine",
"children": []
},
{
"typeName": "deformFlare",
"children": []
},
{
"typeName": "deformBend",
"children": []
}
]
},
//...
]}
//...
}