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

SOLARISでのアトリビュート削除

SOLARIS 内でのアトリビュート処理でいろいろはまったので
SOLARIS でのアトリビュート削除についてメモしておきます。

まず、やりたかったこととしては、SOLARIS で Prim に追加された Attribute を削除したい
ということです。

USD のアトリビュートはざっくりと分けると 2 パターン存在しています。
1 つ目がスキーマに定義されたアトリビュート。
もう一つが、それ以外のカスタムアトリビュートです。

USD は、複数のレイヤーを「合成」して構築するため
ある1つの USD ファイルを見ただけでは、最終的にどうなるかは決定しません。

たとえば、「 sample_val = 0 」という定義があるスキーマを適応し、
LayerA.usd と main.usd がサブレイヤー合成されていたとします。
この時、LayerA にはオピニオンはなく、main.usd に「 sample_val = 20」にするという
オピニオンがあります。

このような場合は、そもそもスキーマに対してアトリビュートが定義されているので
アトリビュートの定義を削除することはできません。
その代わりに用意されているのが「Block」です。

例として、このようにノードを作ります。
inlineUSD では

def "sample"(){
int sample_val = 0
}

sample_val = 0 を持つ Prim を作り、 EditProperties で適当に値を編集します。

そして ConfigureLayer を使用して新しくレイヤーを分け、

usd_blockattrib(0,'/sample','sample_val');

Wrangle ノードのusd_blockattribで、指定した Prim のアトリビュートを Block します。

Wrangle ではなく Python で書く場合は

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

prim = stage.GetPrimAtPath("/sample")
prim.GetAttribute('sample_val').Block()

これでも同等の処理になります。

これで InspectLayers を確認すると、

最初のレイヤーには、sample_val = 10 のように、EditProperties のノードの値が入り、
最終的なレイヤーは、

より強いレイヤー側に「None」の値が追加されています。

Block というのはなにかというと、このようにあるレイヤー(EditLayer)に対して
None を指定することによって、弱いレイヤーで編集した値をその名の通り「Block」
する機能です。

これは、Attribute の定義が消えたわけではないですが、
結果としてパラメーターが無効化され、使用できなくなります。

CustomAttribute の場合​

スキーマに定義されている場合に関しては、アトリビュートは消すことはできないので
Block する...というのは順当な気がしますが
CustomAttribute、つまりはスキーマに定義されているわけではなく
その都度 Prim に足したようなアトリビュートであっても
同様に消す手段は(今のところ)見つかりません。

近しい処理としては、

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

prim = stage.GetPrimAtPath("/sample")

for spec in prim.GetPrimStack():
print(spec)
if 'sample_val' in spec.attributes:
spec.attributes['sample_val'].ClearDefaultValue()

削除したい Prim の Stack をチェックして、記述がある部分を ClearDefaultValue()
して回るくらいですが、

その場合も、Attribute の情報は残ります。
このあとに Flatten したとしても消えません。

個人的には Custom の場合は消させてほしい気もしますが
できないのは Houdini の都合というよりも USD の都合なのかもしれません。

対策​

というわけで、基本 Houdini 上では足したものは消せないので
消せないのであれば「初めからそもそも入れない」ことを前提にノードを組む必要があります。
アトリビュートを追加して、後から消したいケースというのがどういう時にあるかというと
途中で処理の都合一時的にパラメーターを保存しておきたかったケースや
SOlARIS から SOP にデータをもっていき、SOP で処理をした結果 LOP 側にもってきて
その結果をアトリビュートにセットし直したいケースなど、
あくまでも一時的なパラメーターとして使い回したいケースが多いはずです。

その場合は、処理を実行する系統と、反映する系統のノードをわけて
最後に実際に出力したい Stage で、アトリビュートのコピーを実行します。

両方に /sample Prim があるものとして、(都合不要なアトリビュートが多くある)
片方は、 /sample Prim があるが処理をする前の状態であるとします。

SOLARIS は、SOLARIS 内のノードから、その段階の Stage を取得できます。
Stage とは USD の複数レイヤーが合成された結果のシーングラフを扱うためのものです。

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

srcStage = node.node("../OUTPUT").stage()

# 編集しているStageを取得する
srcPrim = srcStage.GetPrimAtPath("/sample")
tmpAttr = srcPrim.GetAttribute("__tmp_value")

# 実際に反映させたいStageのPrim
prim = stage.GetPrimAtPath('/sample')
prim.GetAttribute('result').Set(tmpAttr.Get())

なので、ある段階(処理が終わった状態)の Stage を、ノードを指定することで取得し
編集結果のアトリビュートを取得します。
そして、現在の Stage(現在のノードの編集可能な Stage)に対して
結果のアトリビュートを追加します。

結果、別の SOLARIS のノードのアトリビュートを、現在のノードの Stage に
持ち込むことができました。

これだと対応できないケースもありますが、
回避できるケースもあるんじゃないかなと思います。