Skip to main content

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 で編集するのが個人的には良いのかなと思います。