チュートリアル / 読んで触ってよくわかる!Mayaを使いこなす為のAtoZ
第101回:フェース数から自動でLOD名をつけるツールを作ろう!
- Maya
- ゲーム
- コラム
- チュートリアル
- 初心者・学生
ゲーム向けにLODのオブジェクトを作るのに、MayaではRetopologize(リトポロジ化)やReduce(削減)で自動的にフェース数を削減できます。またはQuad Draw(クワッド描画)で丁寧に作成することもあります。
作成した後に微妙に面倒なのが名前の付け替えでして…。「object_LOD0」などLODと番号を付けていくのも面倒なら、オブジェクト自体の名前を変えたときに他のLODオブジェクトもリネームして回るのも面倒です。
例として、この画像のように左から3Dキャプチャしたメッシュ、Retopologizeしたメッシュ、Reduceしたメッシュ、バウンディングボックスに変換したものを並べます。
これを、手動でポリゴン数に応じて名前をつける、順番に選択するのはミスをしそうですよね。
そういった面倒をスクリプトで簡単に自動化出来るのがMayaのいいところ!ということで、今回はフェース数から自動でLOD名をつけるツールを作ってみましょう。
フェース数の取り方
今回はフェース数をソートしますので、Pythonで書くと簡単です。
フェース数は polyEvaluate コマンドで簡単に確認できます。
polyEvaluate はメッシュの色々な情報を取得できますのでよく使用します。
faceフラグを使うとフェース数を得られます。
triangleフラグを使うと三角形でのフェース数が得られます。ゲームでは三角形のポリゴン数が実際の処理に関わりますので、triangleフラグで得られたフェース数を使用します。
次のように実行すると、pSphere1のフェース数が分かります。
import maya.cmds as cmds
numFaces = cmds.polyEvaluate('pSphere1', triangle=True)
print(numFaces)
あとは、選択した複数のオブジェクトに対してフェース数を得て、順番に並べて、リネームをしていくだけです!
フェース数を元にソートする方法
Pythonをうまく使うと簡単にできます。
[フェース数, オブジェクト名] という配列を用意して、それをリストにします。
Pythonで配列にsort()を実行すると、その配列が二次元配列なら最初の要素を比べてくれます。
例として、直打ちですが次のようにフェース数を書いた配列を用意してsortを実行します。実行するとリストの中の各配列の「1つ目の要素」を比べて、数が少ない順に変わります。
meshNumList = [ [760, 'pSphere1'], [12, 'pCube1'], [800, 'pTorus1'] ]
meshNumList.sort()
print(meshNumList)
# [[12, 'pCube1'], [760, 'pSphere1'], [800, 'pTorus1']]
ただ、順番にLOD名を付けていくのには、フェース数が多い方から順に処理していくほうが簡単です。(LOD0, LOD1…というように、フェース数が多い順に番号を振りますので)
ソートの順番をひっくり返すには、sort(reverse=True)として実行します。
meshNumList = [ [760, 'pSphere1'], [12, 'pCube1'], [800, 'pTorus1'] ]
meshNumList.sort(reverse=True)
print(meshNumList)
# [[800, 'pTorus1'], [760, 'pSphere1'], [12, 'pCube1']]
あとはこの順番でリネームしていくだけです!
と言いつつ、そこが一番ややこしいのですが。
フェース数が多い順にリネームしていく
なぜリネームがややこしいかといいますと、次のことを考えないといけないからです。
・フェース数の一番多いオブジェクトの名前はそのまま流用して、他のオブジェクトには「フェース数の一番多いオブジェクト名_LOD#」という名前を付けていく。
・後でまたスクリプトを実行するとして、既にLOD#がついた名前があると同じ名前を付けられないため、事前に別の名前に変えておく必要がある。
…というわけで完成形はこちらです。コードを見ながら辿っていただくほうがわかりやすいと思います。
下のコードをまるごと実行すると、選択したオブジェクトのフェース数を比べてLOD名を付けます。ついでに新しいグループも作成します。
import maya.cmds as cmds
def renameLOD(createGroup=False):
nodes = cmds.ls(sl=True) # 選択を取得
meshNumList = [] # 空のリストを作成
for node in nodes:
# 選択したトランスフォームの下にメッシュが無ければ、対象から外します。
if not cmds.listRelatives(shapes=True, type='mesh'):
continue
numFace = cmds.polyEvaluate(node, triangle=True) # triangleフラグでフェース数が得られます。
meshNumList.append( [numFace, node] ) # [フェース数, ノード名]で配列に追加。
if len(meshNumList) == 0:
# 有効な対象物がなかった場合はここで終了。
cmds.warning('メッシュのあるトランスフォームが選択されていません。')
return
# 最初に登場する「等しくない要素」、つまりフェース数をもとにソート。reverse大きい順にする。
# ただし、もしすべてが同じフェース数であると、2つ目の要素(ノード名)でソートされてしまうので注意。
meshNumList.sort(reverse=True)
print(meshNumList)
baseName = meshNumList[0][1] # リネームのベースとなる名前は、フェース数が一番多いノードの名前。
cnt = 0
# 既に同じ名前がついている場合に対処するため、先に一度名前を適当に変えておきます。
for elem in meshNumList[1:]: # 2つ目の要素以降でループ
newName = cmds.rename(elem[1], 'tempName#')
elem[1] = newName # 新しい名前でリストを更新します。
# 二回目のループで、LOD名を付けていきます。
for elem in meshNumList[1:]: # 2つ目の要素以降でループ
newName = cmds.rename(elem[1], '{}_LOD{}'.format(baseName, cnt)) # baseName_LOD# で名前を付けていきます。
elem[1] = newName
cnt += 1
# 必要であればグループを作ります。
if createGroup:
objects = [x[1] for x in meshNumList]
cmds.group(objects, name=objects[0]+'Group', world=True)
if __name__ == '__main__':
renameLOD(True)
こんな感じになります。
フェース数が一番多いメッシュだけリネームすれば、ほかは簡単にリネームできます!
まとめ
今回はシンプルなパターンでのLODリネームツールを作成しました。
もっと便利にするために、これを発展させる色々なアイディアが出てくるはずです。
例えば…
・グループを選択したら、その中でLODのリネームを行う。
・選択したオブジェクトがグループの中なら、そのグループ内のオブジェクトに対してリネームする。
・「フェース数の一番多いメッシュ.message → LOD用のメッシュ.何かしらのダイナミックアトリビュート」というノードの接続を作成しておき、後から簡単にLODを見つけてリネームし直すようにする。
・シーン内にあるオブジェクトを不特定に選んで実行したいなら、バウンディングボックスやオブジェクトの位置が同じような位置にあるかを確認して、LODのリネームを行う。
・などなど他にもたくさん!
バウンディングボックスを比べてLOD名をつけるようにすれば、こんな風に適当に選択してもうまくグループ分けできて便利!
更にこれを活かして、LOD番号を指定すると、シーン内でそのLODモデルだけを表示するツールなども用意すると面白そうです。
皆さんも色々と改良して遊んでみてください!