コンテンツにスキップ

SOLARIS で Inherits(継承)を扱う

SOLARIS の中での USD コンポジションアークは、ほぼサブレイヤー・リファレンス・バリアント
の 3 つで成り立っていて、なんか継承の影が薄いなーということで
せっかくなので継承について調べてみました。

継承のノード

まず、SOLARIS 上で継承を使いたい場合は
独自ノードではなく Reference ノード内の ReferenceType を使用します。

ReferenceType の Inherits From First Input を選択すると
Input1 のノードを継承して新しく Primitive を作成します。

Class と Def の違い

継承の説明をする前に、USD の Primitive の定義をする場合、
「def」という Specifier(指定子)を使用しますが
def 意外にも「class」という指定子が存在します。
コレが一体どう違いが出るかというと、

このように、定義するとシーングラフ上に表示されるものの
名前の左側にかがみもちみたいなアイコンが表示され、ビューポートにはなにも表示されません。
class の場合は、あくまでも定義だけで実体を作らず
Reference や Inherits を使用することで
class で定義している Primitive を作成することが出来ます。

継承について

そもそも USD の継承はなにかというと、その名の通りある Primitive の構造を「継承」して
あたらしい Primitive を作成します。
挙動的にはほぼ Reference と同じですが、その挙動には若干の違いがあります。

まず、Reference。
Reference は完全なカプセル化された別のレイヤーを、指定の Primitive に対してロードします。
「カプセル化」がなにかというと、

まず、こんなサンプルを用意します。

1
2
3
4
5
6
7
#usda 1.0
(
    defaultPrim = "Obj"
)
class Cube "ObjBase"{}

def "Obj"(references=</ObjBase>){}

Reference ノードでは、↑ のようなファイルをロードしておきます。
そして、片側の inlineusd で

1
2
3
class Sphere "ObjBase"
{
}

こうします。
この 2 つをサブレイヤーで合成してみます。
「Reference」の場合、

結果は Cube になります。

シーングラフをみても、Class 定義はなくロードされた Primitive のみが表示されます。

Reference で読み込まれてるレイヤー内の構造がカプセル化されているので
あとからサブレイヤーでファイルをロードしたとしても、

合成はされるものの、リファレンス内の構造には影響を与えません。

これを

1
2
3
4
5
6
7
#usda 1.0
(
    defaultPrim = "Obj"
)
class Cube "ObjBase"{}

def "Obj"(inherits=</ObjBase>){}

このように「継承」にしてみます。

すると、サブレイヤーの結果は Sphere に変わりました。
つまりは、Merge ノードを使用して合成した class Sphere "BaseObj" {} が
Reference ノードを使用して読み込んだレイヤー内の class を上書きしている...ということがわかります。
この差が、Reference と Inherits の大きな違いになります。

SOLARIS 内での扱い方

ここまでが USD 的な挙動でしたが
では実際に SOLARIS 上での挙動を調べてみます。

まずはこんな感じで class 指定子で Class を定義します。

Primitive ノードを使用すると、指定の Type で def または class を作成できます。
ので、今回は Cube を Class で作成します。

Inherits は単独のノードではなく Reference ノードのオプションで切り替えできます。
切り替えたら、継承元の Primitive を指定します。

なお、Reference と Inherits の違いとして、Reference は別のレイヤー(別の usd)を指定して
ロードすることが出来ますが、Inherits は同じレイヤー内の Primitive を指定する
必要があります。
ので、別ファイルで定義している場合はサブレイヤーで合成してから inherits =<~~>で
Primitive を指定する必要があります。

で。

Inherits で継承した結果

1
2
3
4
5
6
7
8
9
class Cube "TestClassA"
{
}

def "Inherits" (
    prepend inherits = </TestClassA>
)
{
}

こんな感じの usda が作成されます。

結果のシーングラフ。
Inherits という名前の Cube が作成されました。

この継承したレイヤーを USD ROP で出力します。

そのファイルをRefernceノードでロードすると、
このようにClass定義はなくなり、ファイル内の指定のPrimitive(通常はDefaultPrim)のみが
表示されます。
しかし、表示はされていないものの、継承元の TestClassA クラスはオーバーライド可能で

1
2
3
class Sphere "TestClassA"
{
}

このようなファイルをサブレイヤー合成すると、

こんな感じで合成され

結果はSphereに変わります。

しかし、これを Reference From First Input にしてから再度出力して、
同じノードを確認すると

同じ構成であっても、オーバーライドされずにreference4はCubeのまま

Reference内のClassは隠蔽された状態になります。

これが、ReferenceとInheritsの挙動の差のようです。

loadLayerでの読み込み

レイヤーを読み込む場合、Referenceノードの場合は
Classは隠蔽され、シーングラフには表示されず、継承したクラスをオーバーライドする等は
出来ません。

loadLayer+Referenceノードを使用してリファレンス化した場合

こうすると

こうなります。
ファイルでReferenceを読み込むのではなく
LoadLayerでReferenceノードにつなぐと
あくまでも、同じレイヤー内にあるPrimitiveをリファレンスでロードした状態と同じになり
レイヤー内の隠蔽はされず
この場合は、

1
2
3
class Sphere "ObjBase"
{
}

こんな感じのレイヤーをサブレイヤー合成すると
合成されてオブジェクトがCubeからSphereになります。

LoadLayerで読み込んでReferenceノードでリファレンス化(複製)しても
挙動は同じなのかなと思っていましたが、挙動は違っていて

LoadLayer+Referenceノードにノード接続の場合は
サブレイヤーで合成されたあと、同じレイヤー内にあるPrimitiveをReferenceしている状態で
Referenceノードの場合はPrimitiveで reference=@Path@\</SdfPath>している状態ということか。

というわけで、レイヤーでカプセル化してロードしたい場合は
Referenceノードを使用してロードするのが正解でした。
ノードでの表現方法、まだまだ難しい....`