UsdSkel のメモ

# 背景

USDZ で AR データのアニメーション(excludes timeSamples)を極めたい.

## UsdSkel


機能としては

* (vertex) skinning
* blendshape
* binding

の 3 つっぽい.

skinning と blendshape については, 大まかには glTF や Maya でのそれ(skinning, blendshape)と同じと考えられる.

binding は Maya でいうポーズバインドでしょうかね.
(glTF には対応するものは (explicitly には) ない気がする)

### Schema

https://openusd.org/release/api/usd_skel_page_front.html


および USD のソースコードに schema 定義があります.
ただ, schema 自体はあまり参考にならないかもです.

### 参考情報

https://fereria.github.io/reincarnation_tech/11_Pipeline/01_USD/30_USD_Programming/01_Python/05_usdSkel/

:pray:

### 記述

SkelRoot と Mesh に

```
prepend apiSchemas = ["SkelBindingAPI"]
```

を指定する必要があります.
Mesh 側では

* `rel skel:skeleton` : 対応する `Skeleton` への参照(relation) 
* `int[] primvars:skel:jointIndices` : Joint のインデックス 
* `float[] primvars:skel:jointWeight` : Skin weight
* `matrix4d primvars:skel:geomBindTransform`  : Bind transform

あたりが使えるようになります.

頂点あたりの joint/weight の数は Attribute のメタ情報の `elementSize` で指定します(のはず)

```
int[] primvars:skel:jointIndices = [...] (
   elementSize = 2
   interpolation = "vertex"
)
```

### Skinning

いわゆる linear blend skinning(Vertex skinning, Maya でいう skinCluster)に対応しています.

`Skeleton` では

* bindTransforms : いわゆるポーズ状態にする行列. World matrix になります.
* restTransforms : usdSkel 固有っぽい概念? Maya での bindPose に対応するのでしょうか... こちらは local joint 行列(相対)になります. 基本は bindTransforms を相対にしたものが restTransforms になります. 

`SkelAnimation` で joint に対してアニメーションの変換が指定されていたら, `restTransforms` ではなくて `SkelAnimation` の変換を使うようになります.

sparse = joint の一部だけにアニメーションが割あたっているの意味合いになります.

USD の仕様上だと non-sparse(つまり SkelAnimation での変換がすべての joint に指定されている)だと, `restTranforms` は省略可能とありますが, 実際のところ usdview などではうまく処理されなかったので,
`bindTransforms` と `restTransforms` は両方指定が必要になります(たぶん). 

`bindTransforms` なくして `restTransforms` だけでいいような気もしますが, なんか両方(World と local)ないとだめなケースがあるのですかね...

#### Skin Weights

skin weight 自体はデータとしては必ずしも normalize はされておらず, また(USD の実装では)読み込み時でも自動で normalize は行われません.


### Skinning 行列を求める

* `Skeleton` での local で joint 点の変換
  * SkelAnimation で変換が joint に割あたっていればそれを使う
  * なければ `restTransforms`(local 行列)を使う 
* `Skeleton` での bindTranforms の逆行列
* Mesh 側に `primvars:skel:geomBindTransform` があればそれを書ける
* Mesh に xformOps がああればそれをかける(USD では Mesh 自体も xform を持てる)
* SkelRoot に xformOps があればそれをかける
  * SkelRoot の子が Xform だったらその Xform をかける

でしょうか...
つまり,

```
P' = SkelRootXform x MeshXform x geomBindTransform x inv(Skeleton_bindTransforms) x Skeleton_restTransform_or_SkelAnimation
```


### BlendShape

In-between shape 対応です.
Maya や Blender でのそれとほぼ同じで, USD だけの特殊な定義はないかと思われます.

#### In-between blendshape

Prim 変数に `inbetweens` namespace が付きます. また attribute metadatum で weight を指定します.

例

```
        def BlendShape "twirl_a"
        {
            uniform int[] pointIndices = [
                1, 2, 3, 4, 5, 6, 7, 8, 12, 13, 14, 15, 16, 17, 23, 24, 25, 26, 34, 35
            ]

            uniform point3f[] inbetweens:half = [(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)] (weight = 0.5)
        }
```

`inbetweens:<name>` で pointOffset 相当, `inbetweens:<name>:normalOffsets` で normalOffsets を指定します.

いくつかの sample USD では point3f を使っていますが, schema では vector3f のようです. とりあえず float3 型であればどれでもよいように解釈しないとダメそうです.

`weight = 0` と `weight = 1` は implicity に定義され, `weight = 0` は offset がすべてゼロとして扱われれます.
`weight = 1` は `offsets` attribute になります.

inbetween attribute で weight 0.0, 1.0 指定はエラーとなります.

また, inbeteeen attribute が同じ weight 値を持つ場合もエラーとすることが推奨されています
(重複を許すこともできはするるが, その場合どう扱うかが不明なため)


### Binding

Maya でいうポーズバインドと思われるが...

T.B.W.