コンテンツにスキップ

Specifier

Universal Scene Description AdventCalendar2021 19日目は、
USDの Specifier についてです。

Specifierとは?

Specifierとは、PrimSpecに対して指定されるメタデータで
PrimSpecがどのようにコンポジションされて、どのように解釈されるのか指定するものです。

https://fereria.github.io/reincarnation_tech/11_Pipeline/01_USD/21_stage_layer_spec/

USDは、レイヤーに書かれている「Spec」と呼ばれる主張をコンポジションすることで
最終的案ステージを構築します。
このコンポジションをするときに、各レイヤーに記述されたPrimSpecを
どのように解釈して最終的なPrimにするか
Specifierによってコントロールすることができます。

このSpecifierには3種類あります。

  • def
  • over
  • class

この3つを詳しく見ていきます。

def

defは、Primを定義するもので
多くの場合PrimSpecは「def」で定義されます。

1
2
3
4
from pxr import Usd

stage = Usd.Stage.CreateInMemory()
prim = stage.DefinePrim('/sample')

レイヤーに def で定義された PrimSpecは、

最終的にこのようにPrimとして作成されます。

over

次が over
overは、defとは違い over があるだけではPrimは作成されず
コンポジションによってあるPrimを編集したい場合に、「上書き」をすることを目的にした
PrimSpecを作成します。

1
2
stage = Usd.Stage.CreateInMemory()
prim = stage.OverridePrim('/sample')

例として、このような overだけが定義されているレイヤーを作成します。

作成後、usdviewで確認しても、Primはありません。
over は、すでに def Prim がある場合に Primの値を上書きするので、
overだけの場合、StageにはPrimはありません。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# ベースのレイヤーを作成
base = Usd.Stage.CreateInMemory()
defprim = base.DefinePrim("/sample")
defprim.CreateAttribute('value',Sdf.ValueTypeNames.Bool).Set(False)
base.Export("D:/base.usda")

# ベースのレイヤーをサブレイヤーで合成
stage = Usd.Stage.CreateInMemory()
layer = stage.GetRootLayer()
layer.subLayerPaths = ['D:/base.usda']
prim = stage.OverridePrim('/sample')
primB = stage.OverridePrim('/sampleB')
prim.GetAttribute('value').Set(True)

layer.Export("D:/sample.usda")

では、以下のようにサブレイヤーを使用して
別のレイヤーで def によってPrimSpecがある場合、over で定義された値はオーバーライドされます。

base.usda に sample Prim がDefineされているので
サブレイヤーされたレイヤー側に「over」を記述することで
over に定義されている value = True が、合成されます。

Stageで開いて、現在のTargetLayerに対して編集した場合、
自分より強い主張が別レイヤーに存在する場合は、 over 扱いになります。
これがどういうことかというと
例えば自分より強いレイヤーの def Prim が削除された場合
overだった場合は、 over Primは適応されず Primが削除されます。
対して、over ではなく 編集時も def の場合、強いレイヤーの def Primが削除されても
Primは削除されず、Stageには残ります。

これはとても重要なことで、
例えば レイアウトで配置したあるモデルに対して 次の工程の作業者がマテリアルの調整を
したとします。
しかし、そのモデルが削除された場合
マテリアルの編集は「なかったこと」になってほしいはずです。
defで編集されていた場合は、上流工程で削除されていてもPrimは残り続けます。

Specifierは、Primが最終的にどのように解釈されて解決されるのか。
コンポジションの順序なども重要ですが、 PrimがどのSpecifier を持つかによって
最終的に解決されるStageも変わってくるというのには注意が必要です。
(とはいえ、多くのツールは良しなに計らってくれるし、PythonAPIもそこまで意識しないでも大丈夫ではある)

class

最後に class
classはプラグラミングにおけるクラスと同じで、ある別のPrimにInherits(継承)するための
Primを定義するために使用します。

1
2
3
stage = Usd.Stage.CreateInMemory()
layer = stage.GetRootLayer()
cls = stage.CreateClassPrim('/__class__')

例えば、このように __class__ を class で定義します。
これを usdview で確認した場合は、 over と同様にPrimはありません。

1
2
3
4
5
6
7
stage = Usd.Stage.CreateInMemory()
layer = stage.GetRootLayer()
cls = stage.CreateClassPrim('/__class__')
cls.CreateAttribute('value',Sdf.ValueTypeNames.Bool).Set(False)

prim = stage.DefinePrim("/sample")
prim.GetInherits().AddInherit(cls.GetPath())

しかし、この class Primは Inherits することで、別のPrimに継承することができます。

継承については別途こちらで解説していますので、そちらを参照してください。

def を Inherits することも可能ですが、
class として定義するのはあくまで「共通定義」であることが多く(例:共通のライト、マテリアル等)
そういった共通パラメーターは Traverse や GetChildren で検索には入ってほしくありません。
(overも同様)

そういった場合に、 class で定義することでInheritsするための 定義用Primを
作成することができます。

まとめ

以上 Specifier についてでした。
Stage/Layer/Spec の回で「Spec」について解説しましたが、このSpecifierはPrimSpecから
Primに、Stageに構築する際に「最終的にどのようなPrimになるか」解釈するのに重要な要素になりますので
Stageの記事と合わせて、Specifierについても理解できると
よりUSDのコンポジションを理解できるのではないかと思います。


最終更新日: 2021-12-18 14:55:40
Back to top