コンテンツにスキップ

CompArc(2) 継承

継承とは

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

以下詳しく。

サンプル

シンプルな構造の場合

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#usda 1.0

class "BaseClass"
{
    int hoge = 100
}

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

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

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

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

複数でつかってみる

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#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を使用しなければいけないかといえばそういうわけでもなく

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def "BaseClass"
{
    int hoge = 100
}

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

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

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

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

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#usda 1.0

class "BaseClass"
{
    int hoge = 100

    def "Foo"
    {}
}

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

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#usda 1.0

class "BaseClass"
{
    int hoge = 100

    def "Foo"
    {}
}

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

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#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に定義されたプリムを継承したい場合

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

1
prepend inherits = </ClassName>

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

1
2
3
4
5
6
#usda 1.0

class "BaseClass"
{
    int hoge = 100
}

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#usda 1.0

(
    subLayers = [@class.usda@]
)

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

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

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