メインコンテンツまでスキップ

USDのMeshを編集する

SOLARIS で USD を扱っていると、USD のメッシュを編集したいことがあります。

しかし、SOLARIS 側では編集することができません。
なので、編集したい場合は SOLARIS から SOP に持って行って編集する必要があります。 この場合は SOPImport や SOP 側で LOP Import をするのでも良いですが、
作っと編集したい場合は「SOP Modify」を使用するのがお手軽です。

つかってみる​

使用方法は簡単で、SOLARIS で sopmodify ノードを作成し、入力に編集したいステージをつなぎ
sopmodify ノードをダブルクリックします。
ダブルクリックした中身は SOP になっているので、この中でジオメトリに対する操作を行います。

何も入れてない場合は、1Prim = 1 Primitives 扱いで、いわゆる Pack された状態になっています。 そのため、ジオメトリの変形をしたい場合は、unpackusd を使用してから使用します。

特にジオメトリの編集をしない場合は、sopmodify を抜けた後も基本 Prim の構成なども変わりません。

Mesh をマージする​

これだけだとあまり意味がないので、ジオメトリの編集をしていきます。
よくやりたい操作として、複数の MeshPrim を 1Mesh に結合したいケースがあると思います。

サンプルとして、このように位置をずらした Cube を 2 つ作ります。
これを 1 つの Mesh に変換したいとします。

SOP > LOP にジオメトリを持って行く場合
Primitives の「name」と「path」アトリビュート単位で Mesh が分割されます。

s@path = "/testPrim";
s@name = "testPrim";

なので、まずはすべての Primitives の name と path を上書きします。

実行すると、このようにこれまでの CubePrim がのこったまま Mesh が作成されます。
CubePrim を Hide した状態で、状況を確認すると、

1 つの Cube のみが残っている状態になります。
これはわかりにくいのですが、位置をずらしておいた Cube が同じ位置にずれてしまい
1 つの Cube だけが見えている状態になります。

わかりやすいように片方を RubberToy にしてワイヤー表示にするとこのようになります。

これはどういうことかというと、 Point のアトリビュートに usdxform というアトリビュートがあるのですが
これが、ある USD の Prim の Transform を持っていて
SOP Modify した後に TOP に戻るときに usdxform を適応している影響で
結果元の Prim の Transform 分だけ位置がずれてしまっているわけです。

元の Prim の Transform を維持したい場合はそのままでも問題ないですが
今回のように Mesh を結合したい場合は、この usdxform を消しておかないと
意図した結果になりません。

デフォルトでは、Apply Inverse USD Transform to Unpacked Geometry が ON になっているので
OFF にすることで、usdxform を切ることができます。

あるいは、SOPModify 側の attribute delete で point の usdxform を削除しておきます。

これで、マージされた Mesh が出来上がりますが、このままだと元の CubePrim も残ってしまうので
元の Prim を削除します。

どれを削除するかをわかりやすくするため、GraftStage で指定 Prim 以下の子に入れて
RestructureSceneGraph ノードを使用して、GraftStage で作成したグループを削除するようにします。

GeomSubset​

マージはするけれども、マテリアルアサインは残したい といった場合は
GeomSubset を作成してマテリアルをアサインすることで、Face 単位でのアサインが可能になります。
SopModify では、sopimport 等と同様に、SOP > LOP インポート時のオプションを変更できるので、
SOP の PrimitiveGroup を使用して GeomSubset を作るようにします。

setprimgroup(0,@name,@primnum,1);

s@path = "/testPrim";
s@name = "testPrim";

setprimgroup で、元の Mesh の名前の Group を作っておきます。

Import From SOPs の ImportData にある「Subset Groups」を * にしておくと、

SOPModify 側で作成した Group GeomSubset を作成できます。

マテリアルを維持してみる​

GeomSubset を Group で作れたので、最後にマテリアルも維持した形でマージします。

もう少しまともなやり方がありそうですが、ド直球にやってみます。
まず、マテリアルだけを切り出して Looks 以下にまとめます。

RubberToy と Pig を Merge した結果を ConfigureLayer で FlattenStage して、すべてのコンポジションを Flatten します。
これを入れないと RestructureSceneGraph ノードで親を変えたり削除したりといった操作が
うまく動きません。

これで、mtl 以下をすべて Looks に移動し、

Looks 以外の Prim をすべて削除します。
プリミティブパターンで /* ルート以下の Prim のうち、 /Looks 以外を削除するように書いています。

これで、マテリアルだけ切り出しできました。
なお、今回は同名 Prim に関しては無視しています。

次に SOPModify 側。

setprimgroup(0,s@usdmaterialpath,@primnum,1);

s@path = "/testPrim";
s@name = "testPrim";

string matName = split(@usdmaterialpath,"/")[-1];

s@usdmaterialpath = "/Looks/" + matName;
s@materialBind = matName;

setprimgroup(0,matName,@primnum,1);

すべての Mesh を 1 つの Mesh にマージしたのち、マテリアル単位の Group を作り、
materialBind と usdmaterialpath を指定しておきます。
今回は元 Material を Looks 以下に移動した前提で設定しています。

node = hou.pwd()
stage = node.editableStage()

from pxr import Usd,UsdShade

for geom in stage.GetPrimAtPath("/Geom/testPrim").GetChildren():
matPrim = stage.GetPrimAtPath("/Looks/" + geom.GetName())
mat = UsdShade.Material(matPrim)
UsdShade.MaterialBindingAPI(geom).Bind(mat)
geom.GetAttribute("familyName").Set("materialBind")

最後に、GeomSubset と同名の Material が必ずあるという想定で、Python で
マテリアルをアサインします。

GeomSubset の familyName に対して materialBind を入れておかないと動かないので
それだけ注意が必要です。

最後に、GraftStage で Root をつくれば、GeomSubset 化した単一 Mesh のシーンの完成です。

ここまでできたら、あとは SOPModify 側で Remesh したりいろいろ編集したりしても
元を維持した形でマージした Mesh を持ってくることができます。

まとめ​

今回は非破壊を無視して、全力で破壊的処理をして SOP 側でジオメトリを編集する方法を試しました。
SOPModify を活用すれば、だいたいの処理を SOP 側で実行して後処理を LOP 側でする...みたいなことが可能になります。

LOP 側と SOP 側で何をするかというのは難しい問題ですが
ジオメトリの操作などに関しては SOP 側でやるのが圧倒的に楽だし、
すでにあるアセットに対して何かしら処理をするのであれば
LOP 側を起点にジオメトリ処理を SOP Modify で編集するのが個人的には良いのかなと思います。