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

CompArc(2) 継承

継承とは

コンポジションアーク 2 つめは「継承(Inherits)」
これは、プログラミングにおける継承と同じで、あるプリムのプロパティやその値を引き継ぎつつ
別のプリムを作ります。
特徴として、ある継承をしたいプリムに対して、 継承の指定は SdfPath で行うこと があげられます。

以下詳しく。

サンプル

シンプルな構造の場合

#usda 1.0

class "BaseClass"
{
int hoge = 100
}

def "Fuga"
(
prepend inherits = </BaseClass>
)
{
}

継承を使った場合のレイヤーの最もシンプルな例がこちら。

これを usdview で読み込むと、このようなシーングラフが表示され

アトリビュートはこのようになります。
Fuga プリムにはアトリビュートがありませんが、BaseClass を継承することによって
BaseClass のアトリビュートが Fuga のアトリビュートを引き継いでいることが分かります。

複数でつかってみる

#usda 1.0

class "BaseClass"
{
int hoge = 100
}

def "FugaA"
(
prepend inherits = </BaseClass>
)
{

}

def "FugaB"
(
prepend inherits = </BaseClass>
)
{
int hoge = 200
}

クラスを使用すると、1 つのデフォルトプリムを複数のプリムで共有することができます。
ので、このようにベースのプリムを複数のプリムで使用すると

このように 2 つのプリムができ、両方とも hoge アトリビュートを持ちますが

片方の hoge アトリビュートはこのように Local の値( hoge = 200 )で上書きされているので
200 になっていることが分かります。

def と class の違い

上のサンプルを見ると、継承元のプリム定義は「class」になっているのがわかるかとおもいます。
ですが、継承を使用するときは class を使用しなければいけないかといえばそういうわけでもなく

def "BaseClass"
{
int hoge = 100
}

def "FugaA"
(
prepend inherits = </BaseClass>
)
{
}

このように def で定義したとしても

このように継承の機能を使用することが出来ます。
(あるプリムを指定して値を継承させる)

しかし、def を使用するとそれ自体もプリム扱いされるので
シーングラフ上に「BaseClass」が表示されるようになります。

継承元に子プリムがある場合

上の例だと継承元プリムと継承先プリムの関係は=になりました。
では、継承元側に子プリムがあった場合はどうなるか?というと

#usda 1.0

class "BaseClass"
{
int hoge = 100

def "Foo"
{}
}

def "Fuga"
(
prepend inherits = </BaseClass>
)
{
}

このように、継承先にも子プリムが作成されます。

継承先にも子プリムがある場合

#usda 1.0

class "BaseClass"
{
int hoge = 100

def "Foo"
{}
}

def "Fuga"
(
prepend inherits = </BaseClass>
)
{
def "Bar"{
}
}

上の例だと継承先にはプリムがありませんでしたが、では継承先にプリムがあった場合は
どのように合成されるのか?
というと、

このように、継承元の子プリムに継承先のプリムが追加されるようになります。
このあたりの処理は、考え方的にはクラスと関数の関係に近いようで
親クラス側の関数が継承先で使用できる、さらに継承先のクラスに関数を追加することで
拡張する...それと同じなのかなと思います。

#usda 1.0

class "BaseClass"
{
int hoge = 100

def "Foo"
{
string fooVal = "foo!!!"
}
}

def "Fuga"
(
prepend inherits = </BaseClass>
)
{
def "Foo"{
string fooVal = "FOOOO!!!!!"
}
def "Bar"{
}
}

実際に確認してみると、継承先のほうに同じプリムがあると

こんな感じでオーバーライドされます。

別の usd に定義されたプリムを継承したい場合

ここまでで、継承の挙動がなんとなく分かってきたかとおもいますが
見直してみると、継承を指定している部分の

prepend inherits = </ClassName>

この部分を見ると、SdfPath で継承元のプリムをしているのが分かります。
同じレイヤー内(usda ファイル内)ではなく別のレイヤーに記述されているプリムを
継承で扱いたい場合はどうすれば良いか?というと
継承したいクラスの書かれた usd ファイルをサブレイヤーで合成し
その合成のあとに継承をします。

#usda 1.0

class "BaseClass"
{
int hoge = 100
}

例えばこのクラスが class.usda に書かれていたとします。

コレを別のレイヤーで使いたい場合は

#usda 1.0

(
subLayers = [@class.usda@]
)

def "Fuga"
(
prepend inherits = </BaseClass>
)
{
}

こうすると、
先述の合成の原則から L(サブレイヤーによる合成)後、I(継承)が行われるので

同じレイヤーに書かれているのと同じように、プリムの継承をすることが出来ます。