USD(Universal Scene Description) Composition arcs のメモ

USD での Composition の機能について.
Compositon Arcs(合成の環)というたいそうな名前が付いていてややこしいです.

コンポジションアークとは
https://fereria.github.io/reincarnation_tech/11_Pipeline/01_USD/05_comp_arc/

ありがとうございます.

## 種類

* subLayers (root の Stage/Layer meta でのみ)
* inherits
* references, payload
* variants(variantSet, variantSets)
* specializes
* (over)

## subLayers

C++ での include のようなもの. 
細かいところでは LayerOffset(T.B.W.) を指定できたりする.

## inherits

C++ での継承のようなもの.PrimSpec meta で指定
Path で継承したい PrimSpec を指定.
文法上は複数指定できたり, ListEditing 点けたり出来るがどう解釈されるんでしょうかね.
e.g.

`add inherits = [</base>, </bora>]`


## references

Maya, Max とかでの xref みたいなもの.
subLayers のように外部の usd ファイルを取り込める. 

append, prepend で先に読むかあとで読むかも指定できる.

ただ, prepend にしても attribute の上書きはできないです(下記 LIVRPS 参照)

```
#usda 1.0
def Cube "cube0" {
    double size = 4.0
}
```

```
#usda 1.0

def Cube "cube0" (
  append references = @cube.usda@</cube0>
)
{
  double size = 10 
}
```

とあると, 

```
usdcat references-prepend-cube-000.usda 
#usda 1.0

def Cube "cube0" (
    append references = @cube.usda@</cube0>
)
{
    double size = 10
}
```

と, Local のほうが強いので Local attribute の設定 `size = 10` が採用されます.

local の設定を上書きしたい場合は, `over` を使うしかない... カナ?
 
## payload

`references` と似ているが遅延ロードできる.

https://fereria.github.io/reincarnation_tech/11_Pipeline/01_USD/09_comp_arc_reference/

## variant

キーワードが 3 つあってややこしいです.

variants, variantSets は PrimSpec の metadatum で指定.

`variants`, `variantsets` (最後に `s` が付くことに注意) は PrimSpec meta で指定し, どれが variant になるかを指定する感じ.
`variantSet` (`s` が無いことに注意) は PrimSpec の本体(?)で指定し, 実際に切り替えできる要素を指定する.
variantSet には PrimSpec の階層構造を持つこともできる. また再帰的に `variantSet` を使うこともできる.

prim の meta も定義できる.

```
    variantSet "shapeVariant" = {
        "Capsule" ( active = true ) {
            def Capsule "Pill"
            {
            }
        }
```

ただ, `variantSet` は Meta に定義することはできない

```
# NG
#usda 1.0
(
    defaultPrim = "hello"
)

def Xform "hello" (
    variants = {
        string shadingVariant = "green"
        string bora = "red"
    }
    prepend variantSets = "shadingVariant"
    variantSet = { "bora" { active = true } }
)
{

}

```

### Path での指定

https://graphics.pixar.com/usd/release/glossary.html#usdglossary-path

`{variantname=value}` で指定できます.

e.g. 

``/Root/Child/Grandchild{modelingVariant=withCargoRack}/GreatGrandchild>`

## Specializes

C++ のテンプレート特殊化みたいなもの.
あんまり使われることはない?...

https://fereria.github.io/reincarnation_tech/11_Pipeline/01_USD/09_comp_arc_specialize/


## over

```
over "myprim" { 
  ...
}
```

で, `def "myprim"` の設定を上書きすることができます.
対象となる prim がない場合は, 具現化(シーン(Stage)のプリミティブとしては現れない)

ただ, over と def は同じ名前(path)では USD 内では共存できません. subLayers, references などで外部を読み込んだものに対してのみ over は作用します

```
#usda 1.0
# NG
def Cube "cube0" {
    double size = 4.0
}

over Cube "cube0" {
    double size = 5.0
}
```

```
#usda 1.0
# OK
(
  subLayers = [@cube.usda@]
)

over Cube "cube0" {
    double size = 5.0
}
```

usdcat で `--flatten` で合成すると, `cube0` は def になり size 5.0 になります.

```
$ usdcat --flatten over-cube-001.usda
#usda 1.0
(
    doc = """Generated from Composed Stage of root layer /mnt/data/work/tinyusdz/tests/usda/over-cube-001.usda
"""
)

def Cube "cube0"
{
    double size = 50
}
```

over で Prim のタイプ(Cube, Mesh, ...)が指定されない場合は `def` の設定を利用します.
over でタイプを変えることもできます.

たとえば↑で

```
over Sphere "cube0"
{
   ...
}
```

とすると Sphere プリミティブに変わります.
(ただ, Schema が合わない property になった場合は usdview などでなんかエラーがでるかもしれません)

## ListEditing

たとえば references で usd を読み込む場合, PrimSpec(`def` とかでのローカル定義) より 先に読み込むか(prepend), あとで読み込むか(append)を指定できます. `delete` で削除というのもあります...

## 合成の評価順

LIVRPS 原則に基づく.

https://graphics.pixar.com/usd/release/glossary.html#livrps-strength-ordering
https://fereria.github.io/reincarnation_tech/11_Pipeline/01_USD/05_comp_arc/#livrps


Local, Inherits, VariantSets, References, Payloads, Specializes

リヴレプスと覚えましょう.

USD からみたとき, 呼び出し順は L, I, V, ... で, 評価(解決)の順は S, P, R, ... という感じ?
めんどいネ...

ただ, VariantSet では Specializes は処理されません.

references, payload の場合, ListEditing の append, prepend も影響する気がする?

## 評価のタイミングについて

USDA, USDC を読んだ時点では実際には合成処理は行わず, `Flatten` 操作で合成が行われます.
(`Compose` とかの名前のほうがシックリくる気がするけど..)

`payload` のある場合は, 実際にデータが必要になったときにロードと合成処理(Maya とかでの xref と同じ)が行われる.. はず.

また, Asset Resolver(アセットファイルの探索とか管理)のステートにも依存します(同じファイル名であるが, アセットの基点ディレクトリを変えることで異る中身の USD　を呼んだりとかもあり得る)

https://graphics.pixar.com/usd/release/api/ar_page_front.html

### Stack, Index 用語

ややこしいですが, pxrUSD での Stack は順序あり配列(`std::stack` みたいなスタック構造ではなくて, `std::list` 相当), 

https://graphics.pixar.com/usd/release/glossary.html#usdglossary-index

Index は整数インデックス(id)ではなくて, Composite したあとの結果を指すオブジェクト, という意味合いです.
 

#### LayerStack

https://graphics.pixar.com/usd/release/glossary.html#usdglossary-layerstack

順序ありの Layer のリストです.
順序は `subLayers` とかでの配列での定義順です.

自分自身が最上位になります.

たとえば `sublayers-001.usda` が以下になっていると,

```
#usda 1.0
(
  subLayers = [
    @suzanne.usda@, 
    @cube-000.usda@,
    @cone-000.usda@
  ]
)
```

LayerStack は

![Screenshot from 2022-09-28 20-54-47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36275/b836c782-c7fa-c98e-2ed5-a1d92f044afd.png)

のようになります.

#### PrimSpec, PrimIndex, PrimStack

PrimSpec : Composite 前の状態(USD に記載された"部品"の状態)
Prim : 合成後に実際に出来た Prim
PrimStack :  PrimSpec の配列(リスト)
PrinIndex : PrimSpec, PrimSpec などを Composite して評価したあとの状態. 最終的に PrimIndex を評価して Prim ができあがる感じ? 

#### PropertyStack

Property も Stack がありますが,

https://graphics.pixar.com/usd/release/glossary.html#propertystack

Value resolution(実際にアトリビュート値を取得)には使わず, デバッグなど用です

## 実装者からみての処理

TinyUSDZ で Composition の処理を考えます.


自レイヤー : いわゆる USD ファイル

W.I.P.

* 最初に読み込まれる USD をパースする(これを "自レイヤー"とする)

以下を再帰的に処理していく.

* 自レイヤーの subLayers を解決(USD ファイル読み込み)
* 自レイヤーの Specializes を解決
* 自レイヤーの Payload を解決(読み込みがリクエストされたら)
* 自レイヤーの References を解決
* 自レイヤーの VariantSet を解決
* 自レイヤーの Inherits を解決
* 自レイヤーの `over` PrimSpec を解決




## Crate format

Variant がややこしいです.
それ以外は straightforward な感じです.

## References

`Reference` という型が用意されています.
customData 対応

## Payload

`Payload` という型が用意されています. ほぼ `Reference` と同じです.
0.8.0 から LayerOffset 対応
customData は対応していません.

### Variant

`VariantSelectionMap` : `map<string, string>`

SpecTypeVariant, SpecTypeVariantSet があります.

variantSet 内の Prim, Attribute はそのままいつもの Field 定義のようですが... T.B.W.

## Inherits

PrimSpec metadataum に `inherits`, `inheritPaths` があります.
`inheritPaths` は内部での整合性チェック用でしょうか.

## Specializes

こちらも PrimSpec metadatum に `specializes` があるだけです.

## USDZ AR 拡張(sceneLibrary)

USDZ AR 拡張では, `sceneLibrary` でシーンの切り替え(Variant 相当)をすることができます.

https://developer.apple.com/documentation/realitykit/scenelibrary?changes=_2

* シーン切り替えできるのは root の Prim の kind を `sceneLibrary` にする
* 切り替えするシーンは `over` で定義(プライマリのシーンは `def`)
* Prim meta に `sceneName` でシーン名を記載可能
 
Variant よりもこちらのほうが理解しやすい(and 実装しやすい)ですね.

TinyUSDZ ではとりあえず読み込みは対応しています https://github.com/syoyo/tinyusdz/issues/36
(Handy なシーン切り替え機能はまだ)