USD(Universal Scene Description)のデータ構造のメモ(書き始め)

USD なんかいろいろ用語多かったりしてめんどい...

DCC(データ交換フォーマット)として USD を使いたいとき用のデータ構造のメモです.

## 関連情報

自前 AR アプリとかで USDZ を直接読むための USDC file format のメモ
https://qiita.com/syoyo/items/dbfecbc16468e6108d0d

UsdSkel のメモ
https://qiita.com/syoyo/items/94b9a43a6a6e3c251664

用語
https://graphics.pixar.com/usd/release/glossary.html

## 一般

```
def Mesh "MyMesh" ( ) {
  point3f[] position = [(0.0, 0.0, 0.0), ...]

  float2[] primvar:st = [(0.0, 0.0), (1.0, 0.0), ...] (
    interpolation = "vertex"
  ) 
}
```

`def` `Prim データ型` `名前` でノードを定義します.
Prim データ型は指定しない場合 `GPrim` (Generic primitive? 汎用のプリミティブ型)になります

`()` でメタ情報を付与できます.
これは Stage(.usd ファイル先頭), GPrim と, `primvar:uv` のような Attribute の両方で付与できます.

メタ情報には, `customData` でカスタムデータを付与できます. ただこの場合使える型(float, point3f, ...)は限定されているようです.


.usd(.usdc or .usda)のファイルは Stage と呼ばれるようです.
上記 GPrim でのメタで, 他の .usd を参照するみたいなこともできます(Composition Arc)

### スキーマ

データ構造はスキーマ(schema)として, これも USD(USD ASCII) で定義されています
(JSON とかのほうがありがたいのう)

スキーマのファイルは USD の repo にあります.
e.g.

```
<USD>/pxr/usd/usdGeom/schema.usda
```

ただこれらは C++ コードの include を含んでいます.
ここから usdGenSchema で生成されたものが `generatedSchema.usda` としてあります.

### Shader 関連

Shader 関連は usdImaging(Hydra?) に分類され, コアとして schema は定義されていません(ややこしいですね).

PrimvarReader などのシェーダの schema は

`USD/pxr/usdImaging/plugin/usdShaders/shaders/shaderDefs.usda` にあります.


## モジュール構成

DCC としてメインの要素

* usdGeom : 形状データ
* usdLux : ライト
* usdShade : マテリアル・シェーダ(ただし概念的なコンテナとしての定義のみ)
* usdImaging : 実際のシェーダ, テクスチャ定義関連
  * 最近は usdShaders にリネーム? 
* usdSkel : スキニング, Blendshapes

非コア的な要素

* usdVol : ボリュームデータ
* usdHydra : Hydra(レンダラへのインターフェース)
* usdRender : レンダリングの設定とか
* usdUI : GUI 要素?
* usdMedia : 音声など(現状は spatial audio のみ)
* usdPhysics : 物理
* usdUtils : 

## 大まかな構成

ノード + プロパティ(変数)という感じで, 基本 glTF 的な感じです.

## Prim

ノードに相当します. Property や子のノード(Prim)を持つことができます.

### Specifier

Prim には三種類の Specifier があります.

`def` : Prim の定義(通常はこれ)
`over` : Prim 定義の上書き. 上書き対象(`def`. `class` も?)がない場合はなにも Stage には追加されない.
`class` : ユーザー定義の抽象 Prim の定義. 継承されない限り Prim にはならない. `class Xform` みたいに既存の Prim 型に class をつけてもエラーにはならない(どう処理したらいいじゃろうか)

### Model

Prim とかの上位概念っぽい. Model から Group が作れたりする.

### Kind

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

Prim(ノード)のカテゴリを Prim のメタデータで指定します. 基本は `"component"`?

* model : ノードの基本型. 抽象的な型なので実際の USD データには出てこないっぽい?)(`def` で型なしが model に該当するのかしらん)
* group : ほかの model(assembly, component, subcomponent?)のグループとなる. https://graphics.pixar.com/usd/release/glossary.html#usdglossary-modelhierarchy Model Hierarchy に従う
* assembly : これもグループとなるノード. `group` との関連がよくわからずややこしい. Prim や references を子に持つことができる, `kind = group` のノードも子に持つことができる. 
* component : Kind の "leaf"(末端)となるノード. 子には `subcomponent` しか持つことができない. GPrim は基本これかしらん.
* subcomponent : component の子であることを imply するようなもの? GeomSubset ノード(GeomMesh の子である)などがこれに該当するのかしらん?

### Active

Prim を有効, 無効にする. deactivate でも, シーングラフ上にはデータとしては存在するまま. Maya でいう Hide(invisible) みたいな感じ.
 

https://graphics.pixar.com/usd/release/glossary.html#active-inactive





### PrimSpec

Prim specifier の略カナ?

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

https://zenn.dev/remiria/articles/usd-stagelayer#%E3%82%B9%E3%83%9A%E3%83%83%E3%82%AF

Prim と PrimSpec の関係がややこしいですが, PrimSpec を compose(コンポジット)して最終的にシーングラフ要素として出来上がったのが Prim になる感じです.
(Prim になる前の部品(仕様)の状態が PrimSpec)

したがって Composition arc(コンポジット処理)は PrimSpec に対して行われます.

Maya で言えば, たとえば Sphere プリミティブを作ったときの Construction history の最初の状態(仕様)が PrimSpec という感じでしょうか.

## Opinion

意見? たとえば Attribute で `float radius` とあれば `radius` は float 型の変数と言い張っている.
コンポジットとかで名前が同じでも型がちがったりするとき,  LIVRPS ルールに従って解決する.

https://graphics.pixar.com/usd/release/glossary.html#livrps-strength-ordering 


### Property

Attribute or Relation のベースクラスになります.

https://graphics.pixar.com/usd/dev/api/class_usd_attribute.html

https://fereria.github.io/reincarnation_tech/11_Pipeline/01_USD/17_property_attribute_relation/

Property は

* Relation(`rel`) : material のアサインなど(e.g. `rel material:binding = </path/to/mat>`)
  * `rel myrel` みたいにターゲットのパスがないカラの定義もできはする(基本的にはこのような定義はコメントのようなもので, 無視してもかまわない(はず))  
* Attribute
  * `float myval = 1.0` : (スカラー?)値のアサイン
  * `float myval.timeSamples = ...` : TimeSamle 値のアサイン
  * `float mycal.connect = </path/to/mat>` : ターゲット先のパスの値を参照する(`.connect` では Path の配列も指定可能(ただあんまり使うケースはないもよう))
  * `float outputs:rgb` : Attribute の定義だけ(シェーダなどで `.connect` 先を作るのに使われる) 

に分類されます.

`.connect` は仕様上は循環参照ができてしまいます(詳細は後述)

## データ型

https://graphics.pixar.com/usd/dev/api/_usd__page__datatypes.html

color3f など, 中身のデータ型は同じであるが, 役割がことなる Role type というのもあります.
(実際としては, たとえば頂点カラーは color3f or color4f 型とすることで, より適切な型を指定することで多少わかりやすくする程度の効果だけかとは思われる)

### Token

`"mytoken"` のようにダブルクオーテーションで囲います.
識別子のような文字列に使います(`string` 型との違いは, 改行などを持つことができない, 特殊記号など使える文字に限定がある)

`float`, `xformOp:translate`, などがトークンになります.

### String

Token と同じように `"strval"` のようにダブルクオーテーションで囲います.

さらに, Ascii では, Python のように `"""` で無変換の(改行を持つ)文字列を扱うことができます.

```
string myval = """
bora
dora
"""
```

USDC(Binary)では triple quote と single quote で違いはありません.


## Attribute

いくつかの Attribute は, timeSamples としてアニメーションデータ(時間とデータのリスト)とすることが可能です.

https://graphics.pixar.com/usd/release/tut_xforms.html

デフォルトの値と `.timeSamples` とで両方持つことができます.


## uniform(Variability)

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

`uniform` が付くのは主に時間軸で変化しないアトリビュートであるのを指示する.
(スキーマレベルでは定義されるが, 効率のためにコア(実装側)ではヒントとして解釈される程度っぽい)

たとえば `token visibility` は `uniform` が付かない(時間軸で可視不可視できる)が, `uniform token purpose` には付いたりする.

## Attribute block

`none` を指定するところでブロックすることができます.

https://graphics.pixar.com/usd/release/glossary.html#attribute-block

### TimeSamples

TimeSamples も `None` を持つことができます


`None`: 指定した時間軸にはデータが無いことを指定する(blocked).
また, Block は reference したものに対して特定の領域だけ上書きというのはできない.

```
def Sphere "BigBall"
{
    double radius.timeSamples = {
        101: 1,
        102: 2,
    }
}

def "DefaultBall" (
    references = </BigBall>
)
{
    double radius.timeSamples = {
        101: None,
    }
}
```

この場合 `DefaultBall.radius` は全部 blocked となる.
(BigBall の `[-inf, 101)` だけを block するのでは無い)

`delete` ListOp でサンプルを消すのも指定できたような?

## Attribute

primvar(primitive variable)とその関連ステートを持ちます.
実際にはこの Attribute(primvar)がそれぞれのノード(GeomMesh, Xform, Material, ...)を構成するコアとなります.

## References

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

xref みたいなものです. 基本は他の usd ファイル(ステージ?)を読み込む機能な感じです.

## Relationship

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

JSON pointer みたいなものですかね. 
texture mapping のときの prim 側のテクスチャ座標がどれかを指定するときなどに使います.

### Connection

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


Relationship と近いのに `Connection` があります.

Connection は, Attribute では変数に `.connect` で指定しています. シェーダグラフのように型が決まっているものなどに使います.

Relationship だと型なし(typeless)なので参照されている先を評価しないと型がわからなかったりします.

## PrimVar

Primitive Variable の略です. USD のコアとなるタイプで, 変数みたいなものになります.

慣例として? テクスチャ UV などには `primvars` ネームスペースが付きます.
(Schema で定義されているわけではないが, Schema のコメントで対応しているネームスペースの記述があったりするので, ライブラリ側でいろいろ対応が必要)

```
float2[] primvars:st = ...
```

## 変数名, ネームスペース

`:` でネームスペースをつけることができます. `primvars:displayColor`.

Xform ノードの, `xformOp` など, 使うネームスペースの指定(また, xformOp は使える型が決まっている. `xformOp:translate` は float3 or double3 型と決まっている)があったりしますが, schema では定義されていないので注意です(schema のコメントにはどのネームスペースを使うか記述あったりはする)

### userProperties

DCC ツールなど用にカスタムの attribute 情報付与したい場合は, `userProperties` ネームスペースをつけるのが推奨のようです.

e.g.

`custom string userProperties:blenderName:data = "Cube"`


### prefix, suffix

Xform には `!invert!` の prefix があったりします(行列処理を invert する)

`.connect` で connection を指定.
`.timeSamples` でアニメーションデータを指定

https://graphics.pixar.com/usd/release/tut_xforms.html

suffix として `.` をつけられるのはこの `.connect` と `.timeSamples` のみのようです(他の名前をつけると syntax error となる)

## Custom attribute

スキーマに定義されていないものは `custom` qualifier をつけます.

```
custom float myval = 1.3
```

ただ, `usdchecker` などでは厳格なチェックはされていないよう? で custom つけなくてもユーザ定義の変数(Attribute)

### Metadatum

Node(e.g. GPrim)や Attribute はそれぞれメタデータを持つことができます.
また Stage(.usd のグローバル/ヘッダの要素に対応) もメタデータを持つことができます. たとえば upAxis などもはメタデータになります.

`interpolation` は, UsdStage などオブジェクト本体(?)に持つのと, PrimVar の meta として扱われるのがあってややこしいので注意ください.

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

#### Custom data

メタデータにさらに任意の情報を付与するのに `customData`(dictionary 型) を使います.
(Stage の場合は `customLayerData`)

`customData` で使える型は USD の仕様では限定されているようです(TODO: ソースコードのどこで限定されているか調べる).
(bool, string, float あたりの基本的な型は使える).
また, dictionary データも更にネストして含めることができます.

```
def "Xform" bora (
  customData = {
    bool zup = 1
    dictionary Blender bmeta = {
      bool generated = 1
    }
  }
 ) {
  ....
 }
```

## Xform

glTF とかのシーングラフでいういわゆる node です.
変換行列 or `traslate` などの変換 op)を持ちます.

xformOpOrder で変換の順を記述できます.

https://qiita.com/takahito-tejima/items/4c30dd882ea3fdd737fb

### namespace

`xformOp:translate` では float3 or double3 と, 取れる型が決まっています.
(schema では決まっておらず, 実装側で対応が必要).

また,

`xformOp:translate:mytx:bora` のような namespace がさらに付いている場合も `xformOp:translate` の namespace が付く変数(つまり float3 or double3 型)として取り扱われます.
 
### prefix

`!invert!` で変換を invert できます!

`!invert!xformOp:translate:scalePivot`

基本は値を negate ですが, matrix(`transform`) の場合は逆行列を求めることになるんじゃろか :thinking:

### special token

`!resetXformStack!` もあります. これは最初の要素だけに付くっぽい. 
(例が無いのでよくわらぬが, `["!resetXformStack!", "xformOp:translate", ...]` みたいな?) 

行列は親のノードを継承しますが, `!resetXformStack!` ではこの変換スタックを取り除きます(つまりこれが定義されたノードは identity matrix から始まる).

親の変換を無視したいときに利用します(ライトや skinning の場合に使うのかな?)


## Visibility

`interited` or `invisible` のみです.
`inherited` は親の visibility 設定を引き継ぎます. 
親が不可視で, 子が可視というケースは無いので, `visible` はありません.
root 親のデフォルトは `inherited`(つまり可視)です.

## extent

geometry などのバウンディングボックスです.
基本的にはアプリ側で計算しないといけない?

## Purpose

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

主にレンダラ用です.
Prim の描画の使われ方に使います.

## Reference

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

```
string assetPath;
Path primPath;
LayerOffset layerOffset;
Dict customData;
```

T.B.W.

## Scope

パスに名前をつけて階層を管理しやすくする(グループ化)感じの役割をします.
Identity Matrix を持つ Xform と同じです.

## Material/Shader

Material の下に Shader があったり, Shader のノードで `UVTexture` を定義があったりという感じです.

### Texture mapping

UVTexture でテクスチャファイルなどを指定しますが,
USD ですと, UV 座標はを PrimvarReader で Geom から取得して処理, と面倒になっています.



### PrimvarReader

`inputs::varname` で読み取り先(多くの場合は UV texture coordinate)の attribute 名を指定します.
ただなぜか `primvar` namespace は指定しません(Mesh に `primvar:st` とあれば `inputs:varname` には `st` を指定.

https://github.com/PixarAnimationStudios/USD/blob/release/pxr/usd/usdHydra/shaders/shaderDefs.usda

の schema を見ると `primvar` の namespace は除くとあります.

また, `primvarProperty` というメタデータ定義されています. 

```
 sdrMetadata = {
            token primvarProperty = "1"
        }
```

参照する先のデータが primvar の Property であることを示すようです(e.g. `visibility` みたいなアトリビュートは NG になるはず)

### UDIM texture

ファイル名を調整で対応しています.

### two-sided material, per-face Material?

GeomSubset で face をまとめることで per-face material を実現はできます.

Geom を doubleSided にして, メッシュの表と裏に別のマテリアルを割り当てるのはできないっぽ?

### MaterialX

usdMtlx で, MaterialX 形式のマテリアルを読めます.
実体としては MaterialX .xml を利用するだけ? のようです(MaterialX 設定をどう利用するかはツール側にまかせる)


## Stage, Layer

Stage = シーングラフ全体, Layer = Stage 内で個々のシーングラフを構成する要素(たとえば Layer は特定の時間軸に配置することができる)という感じです.

https://zenn.dev/remiria/articles/usd-stagelayer

Layer として .usd を読み込んだ場合一部の Stage Meta(e.g. upAxis)は無視されるのかしらん? 

### Layer Stack

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

T.B.W.

## Plugin 関連

Plugin は USD ではなく JSON で構造記述になります. ややこしいネ...

usd の定義自体 `plugInfo.json` である程度変更することができます. たとえば upAxis のデフォルト(厳密には実装依存?)は Y ですが, これを Z に変えたりできます.

その他は T.B.W.

## Deformer

いわゆる Maya での wrap deformer など.

pxrUSD 自体では schema で定義はありませんが, たとえば Omniverse では拡張としてあります.

https://docs.omniverse.nvidia.com/prod_extensions/prod_extensions/ext_animation-deformers.html


## Composition arcs

`合成の環`というたいそうな名前が付いてややこしいですが,
いわゆる設定のオーバーライドみたいな感じです.
メッシュデータは同じでマテリアル設定だけキャラクターごとに変えたり, などが記述できます.

T.B.W.

## List Editing(ListEdit op)

Compositon などとからんだり, 編集の履歴を残したりのために ListEdit op があります.

https://graphics.pixar.com/usd/release/glossary.html#list-editing

* `append` : prim を composite するときの最後に処理する
* `prepend` : prim を composite するときの最初に処理する
* `delete` : 値を削除する(のを指示する)
* `reset to explicit` : unqualified に相当. 他の weaker レイヤーの listedit op を無視

評価には優先順位があります.

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/

を参照ください.

Prim の Property の場合, Attribute は  List editing できません. `rel`(Relation) だけ適用可能です.

## API Schema

https://graphics.pixar.com/usd/release/glossary.html#api-schema

3 つあります.

### Non-applied API Schemas

よくわかりません. とりあえず普通のユーザにとっては考えなくてもよさそう.

### Single Apply Schemas

Prim のメタデータで `apiSchema` として inject されるものです(たぶん)

```
prepend apiSchema = ["MaterialBindingAPI"]` 
```

など.

### Multiple-apply schemas 

インスタンス名(ネームスペースとして)を取って instanciate して inject できる API schema.
`CollectionAPI` が該当します.

```
prepend apiSchemas = ["CollectionAPI:material:MainMaterial"]
```

`CollectionAPI` は

https://fereria.github.io/reincarnation_tech/11_Pipeline/01_USD/14_usdCollection/

参照ください. light link などもこれで実現できる... はず.

apiSchemas の仕組み上, ListEdit Op は `prepend` のみです(のはず)
(`append apiSchemas`, `delete apiSchemas` というのも一応構文上は OK のようで, `usdchecker` はエラー出さないけど...)