はじめに
Houdini Advent Calendar 2017の急遽な内容変更で、なぐり書きなのは許してください。
漏れっぽいところとか補足(画像)とか後々修正しますん。
内容はMaya -> Houdini -> Mayaで持っていく時の話で、最後にMayaでレンダリングする必要がある場合などに、Mayaに持ち帰って元のマテリアルなどを一通り再アサインさせてレダンリングできる準備を行えるようにするところの部分をスクリプトで構造作っちゃおうというやつ。(なので、断面とかHoudiniで発生したものに関しては今回は除外)
こういうやり方もあるよ的な体で捉えてもらえれば。
Maya
このフローにおいて、ルール付けることは以下のとおり
- Shape名は、最後に必ず「Shape」とつける
- オリジナルのジオメトリということで、「
<なんたら>_model
」ってNamespaceつけとく。ついでに「<なんたら>_from_houdini
」とかいうのもつくっとく
<なんたら>:ROOT
|-- <なんたら>_model:ROOT
|-- <なんたら>_from_houdini:ROOT
以上
Maya -> Houdini
とりあえず普通にAlembicファイルをExportする。
Houdini
Alembic SOPで読み込む。
この際、Geometry > Primitive Groups を Name Group Using Shape Node Name に切り替える。
すると、AlembicをUnpackした際に、MayaでのShape名がそのままHoudini上でGroupアトリビュートとして再現される。
読み込んだら後はHoudini上で好きに作業を行うわけだが、重要なのは、 このShapeグループを書き出しまでずっと保持し続けること!!
後で使うので、壊さないように作業する。
特に注意するのが、Voronoi SOPなどをデフォルトのまま使ってたりすると、outsideのGroupを作るが、これがShapeのGroupと重なってしまい、書き出し時にoutsideの方が優先されて認識されてしまう可能性があるため。
作ってもいいが、作業後はそれらのGroupは削除する事。
Mayaに持っていく前に、保持しているShapeのグループを、Python SOPを使ってPrimitiveの path アトリビュートとして追加してあげる。
import re
node = hou.pwd()
geo = node.geometry()
path_attrib = geo.addAttrib(hou.attribType.Prim, 'path', '')
instance = hou.node('..').name()
base = 'base'
name = 'name'
for prim in geo.prims():
grps = prim.groups()
for grp in grps:
name = grp.name()
base = re.split(r'Shape$', grp.name())[0]
path_name = '/' + instance + '/' + base + '/' + name
prim.setAttribValue(path_attrib, path_name)
これを介して、書き出しを行う。
ROP Alembic Outputで次の設定を行う。
- Use SOP Path: ON
- Build Hierarchy From Attribute: ON
- Path Attribute: path
書き出しを行ううえで以下も同様に気をつけておく。
- 要らないGroupは全て削除
- 要らないAttributeは全て削除(Mayaで使う予定がない限り基本捨てる
- MayaにPointの概念はないので、必要なものはVertexにPromoteしておく
最終的にはこんな感じのノード構成。
書き出し様にこういった作業用と分離したネットワークを用意するのが吉。
Houdini -> Maya
オリジナルのモデルがあるシーンにAlembicファイルをインポートしてくる。
インポートしてくると、「Exportしたネットワークの名前 > それぞれのShape名」として展開される。
あとはこれにマテリアルをしていくのだけど、データ整理も一緒にしておく。
<なんたら>:ROOT
|-- <なんたら>_model:ROOT
|-- <なんたら>_from_houdini:ROOT # Houdiniからimportしてきたやつをここに突っ込む
|-- <Imported Alembic Cache>
そんでもっていかのスクリプトを実行して、オリジナルジオメトリからマテリアルを再アサインする。
import re
# マテリアルをオリジナルのやつと同じ名前のやつからひっぱってきてセット
def matchMat(rshape, houshape):
material = cmds.listConnections(houshape, sh=True)[0]
if material:
cmds.sets(houshape, forceElement=material)
return True
else:
return False
# Shapeアトリビュートを一致させる。Arnoldとかこういうの必要。
def matchAttr(rshape, houshape):
attrs = cmds.listAttr(rshape)
for attr in attrs:
houshape_attr = houshape + '.' + attr
if not cmds.ls(houshape_attr):
continue
rshape_attr = rshape + '.' + attr
try:
value = cmds.getAttr(rshape_attr, silent=True)
cmds.setAttr(houshape_attr, value)
except:
pass
# オリジナルの方をHIDE
def switchVisibility(rshape, houshape):
cmds.showHidden(houshape)
transform = re.split(r'Shape$', rshape)[0]
try:
cmds.hide(transform)
except:
print('%s is not found.'%(transform))
def main():
cache_roots = cmds.ls(r'*_model:ROOT', type='transform')
# Show top of effect_cache roots
if cache_roots:
cmds.showHidden(cache_roots, above=True)
for root in cache_roots:
print('===== Excuting reassigning shaders of "%s" ====='%(root))
houshapes = cmds.ls(cmds.listRelatives(root), dag=True, type='mesh')
asset = re.split(r'_from_houdini:ROOT', root)[0]
# Namespaces that are pared with from_houdini.
nsps = [asset+'_model']
for houshape in houshapes:
result = None
for nsp as nps:
rshape = nsp + ':' + re.split(r':',houshape)[1]
if not cmds.ls(rshape):
continue
result = matchMat(rshape, houshape)
matchAttr(rshape, houshape)
switchVisibility(rshape, houshape)
if not result:
print('Material for "%s" is missing. Assigning material is skipped.'%(houshape))
スクリプトエディタでやるなら、上を全部叩いて、main()で呼び出してね
おわり