ここ何回かに作った関数を組み合わせて、シーン上にマテリアルのアサインが(フェイスの1枚でも)抜けているメッシュがないか確認するscriptを作りました。同じようなループ処理をいろんなところで繰り返しているので、重そうではありますが一応使えます。
フェイスが多くて重たそうな時は、一部選択して探索領域を狭められるのでそうするといいかもしれません。
使い方

このまま何も選択せず、後述のスクリプトを実行します。(シーン全てが探索対象。)

もしくは、探索したいツリーの上位ノードを選択してやってから、スクリプトを実行します。

結果は scriptログに以下のような感じでも表示されます。
探索対象shape一覧______
pCube1|pCubeShape1
pCube2|pCubeShape1
pCubeShape3
pCubeShape4
pCubeShape5
pCubeShape6
pasted__pCube1|pasted__pCubeShape1
pasted__pCube2|pasted__pCubeShape1
マテリアルアサイン不正のshape一覧__
pCubeShape5
pCubeShape6
処理のポイント
普通にメッシュ一覧をls
で取る場合はインスタンスがあるメッシュは1つのメッシュとしてまとまって返ってきてしまうので、フェイス単位でのマテリアルチェックができません。(正確に言うとできるが、ヒエラルキー上でどこにあるのか探しづらくなる。)その辺をよしなに作ってあります。get_incomplete_mat_shapes
関数以外の注意点は他の関連投稿をみてください。
クイック実行用ソース
scriptパネルやシェルフに登録する想定です。
しかし長いな。。しかし、頑なにモジュール化したりギハブにソースをあげる事を避ける検証シリーズなので、このまま行きます。
(判定に一部問題があったので次の記事で修正をいれています。)
import pymel.core as pm
def has_face_assign(shape):
"""
シェイプにマテリアルのフェイスアサインがあるかどうか確認する
:param shape: チェックするシェイプ
:type shape: pymel.nodetypes.Shape
:rtype: bool
"""
instanceNumber = shape.instanceNumber()
objectGrpCompList = \
shape.instObjGroups[instanceNumber].objectGroups[0].objectGrpCompList
num_face_block = (len(objectGrpCompList.get()))
return num_face_block > 0
def has_object_assign(shape):
"""
シェイプにマテリアルのオブジェクトアサインがあるかどうか確認する
:param shape: チェックするシェイプ
:type shape: pymel.nodetypes.Shape
:rtype: bool
"""
instanceNumber = shape.instanceNumber()
instObjGroup = shape.instObjGroups[instanceNumber]
cons = pm.listConnections(instObjGroup, plugs=False)
for con in cons:
if con.type() == 'shadingEngine':
return True
return False
def is_face_assigned_all(shape):
"""
フェイスアサインの抜けがあって、マテリアル未定義面ができていないか確認する
:param shape: チェックするシェイプ
:type shape: pymel.nodetypes.Shape
:rtype: bool
"""
# compInstObjGroupsからコネクションがSGに伸びていればフェイスアサインに抜けがあってもマテリアルがない面が現れることはない
indices = shape.compInstObjGroups.getArrayIndices()
found = False
for i in indices:
group = shape.compInstObjGroups[i].compObjectGroups[0]
cons = pm.listConnections(group, plugs=True)
if len(cons) > 0 and cons[0].type() == 'shadingEngine':
found = True
break
if found:
return True
# フェイス面ごとにフラグを持ってマテリアルアサイン抜けがある部分を探す
instanceNumber = shape.instanceNumber()
indices = shape.instObjGroups[instanceNumber].objectGroups.getArrayIndices()
numFaces = shape.numFaces()
assined_map = [False] * numFaces
for i in indices:
objectGrpCompList = shape.instObjGroups[instanceNumber].objectGroups[i].objectGrpCompList
faces_list = objectGrpCompList.get() # ex) ['f[0]','f[1:3]']
# 返ってきたフェイス情報が文字列なので力技で面番号のリストに変換する。。。
for faces in faces_list:
faces = faces[2:-1] # ex) '0' or '1:3'
if ':' in faces:
a_ = [int(x) for x in faces.split(':')] # ex [1,3]
a_ = xrange(a_[0], a_[1] + 1) # ex [1,2,3]
else:
a_ = [int(faces)] # ex [0]
# マテリアル設定があった面情報としてTrueをセット
for i in a_:
assined_map[i] = True
#一つもFalseがなかったら全ての面にマテリアルが定義されている
return not (False in assined_map)
def get_incomplete_mat_shapes(select_result=True):
"""
マテリアルのアサインに1フェイスでも抜けがあるシェイプの一覧を返す
:param select_result: 見つかった不正メッシュを選択状態にするか
:type select_result: bool
:return: 不正メッシュ一覧
:rtype: list of pymel.nodetypes.Shape
"""
check_target_shapes = []
selected = pm.selected()
def show_info(title, shapes):
print('')
print (title)
if len(shapes) == 0:
print(u'なし')
for shape in shapes:
print(shape)
# 選択状態のノードがある場合はその中身だけを探索する
if len(selected) > 0:
# ノードのツリーは全部見るので子孫一覧を取る
rels = pm.listRelatives(selected, allDescendents=True)
# 最初の選択ノードも探索対象にする
rels.extend(selected)
for rel in rels:
# meshなら探索対象
if isinstance(rel, pm.nodetypes.Mesh):
check_target_shapes.append(rel)
elif isinstance(rel, pm.nodetypes.Transform):
# 子供がmeshなら探索対象
rel_shape = rel.getShape()
if isinstance(rel_shape, pm.nodetypes.Mesh):
check_target_shapes.append(rel_shape)
# 重複を取り除く
check_target_shapes = list(set(check_target_shapes))
else:
# 選択状態のないノードがない場合は、シーンのmesh一覧を探索するが、
# shapes = pm.ls(dep='mesh') # これだとインスタンスのmeshが一つにまとまってしまい全てが一覧に返ってこないので、
# transform一覧を得てから
transforms = pm.ls(type='transform')
# 子供のmeshだけを集める
check_target_shapes = [x.getShape() for x in transforms if isinstance(x.getShape(), pm.nodetypes.Mesh)]
# ここからマテリアルのアサインチェック
incomplete_shapes = []
show_info(u'探索対象shape一覧______', check_target_shapes)
for shape in check_target_shapes:
if has_object_assign(shape):
# オブジェクトアサインがある場合は不正でない
continue
elif has_face_assign(shape):
# フェイスアサインがある場合で
if not is_face_assigned_all(shape):
# 抜けがある場合は不正である
incomplete_shapes.append(shape)
else:
# なんのアサインもなければ不正である
incomplete_shapes.append(shape)
# 結果表示
show_info(u'マテリアルアサイン不正のshape一覧__', incomplete_shapes)
if select_result:
pm.select(incomplete_shapes)
return incomplete_shapes
get_incomplete_mat_shapes(select_result=True)
del get_incomplete_mat_shapes
del is_face_assigned_all
del has_object_assign
del has_face_assign