USDの実践メモ 02 : メッシュの作成


[前回](https://qiita.com/snowxcrash/items/0120730a2e3a96e39a12)に続き，今回はメッシュを作成します。

## メッシュの作成

次のようなメッシュの作成に取り組みます。Vertex が 6 点，Face が 2 面あります。

![meshgrid_480x360.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659949/6d3a921f-11f4-6270-6376-1f519e5a083b.png)

これを Points（頂点座標），FaceVertexCounts（面の頂点数），FaceVertexIndices（面の頂点ID） の各アトリビュートに落とし込みます。DoubleSided（両面表示）はオフとしました。

```Python
from pxr import Usd, UsdGeom, Sdf

def create_mesh(stage, path):
    mesh = UsdGeom.Mesh.Define(stage, path)

    # Points 
    vList = []
    vList.append((-1, 0, 0))
    vList.append(( 0, 0, 0))
    vList.append(( 1, 0, 0))
    vList.append((-1, 1, 0))
    vList.append(( 0, 1, 0))
    vList.append(( 1, 1, 0))

    mesh.CreatePointsAttr(vList)

    # FaceVertexCounts
    fList = [] 
    fList.append(4)
    fList.append(4)

    mesh.CreateFaceVertexCountsAttr(fList)

    # FaceVertexIndices
    fList = []
    fList.extend((0, 1, 4, 3))
    fList.extend((1, 2, 5, 4))

    mesh.CreateFaceVertexIndicesAttr(fList)

    # DoubleSided
    mesh.CreateDoubleSidedAttr(False)

    return mesh
```

ここで，頂点に色情報（RGB）を与えてみます。
色は指定の頂点以外の空間（たとえば面全体）にも現れてほしいので，補間が可能な Primvar として盛り込みます。補間方法は varying（Bilinear補間）を指定しています。

```Python
    # DisplayColor
    vList = []
    vList.append((1, 0, 0))  # Red
    vList.append((0, 1, 0))  # Green
    vList.append((0, 0, 1))  # Blue
    vList.append((1, 1, 0))  # Yellow
    vList.append((1, 1, 1))  # White
    vList.append((1, 0, 1))  # Magenta

    primvar_api = UsdGeom.PrimvarsAPI(mesh)
    primvar = primvar_api.CreatePrimvar('displayColor', Sdf.ValueTypeNames.Color3f, interpolation=UsdGeom.Tokens.varying)
    primvar.GetAttr().Set(vList)
```

メインプログラムです。

```Python
if __name__ == '__main__':
    # ステージ作成
    stage = create_stage()
    
    # ルートノード作成
    rootNode = create_node(stage, '/World', is_defaultPrim=True)

    # メッシュ作成
    mesh = create_mesh(stage, rootNode.GetPath().AppendChild('Mesh'))

    # USDファイルにエクスポート
    stage.GetRootLayer().Export('./Mesh.usda')
```

出力は次のとおり。usdchecker もパスしました。

```USD
#usda 1.0
(
    defaultPrim = "World"
    metersPerUnit = 0.01
    upAxis = "Y"
)

def Xform "World"
{
    def Mesh "Mesh"
    {
        int[] faceVertexCounts = [4, 4]
        int[] faceVertexIndices = [0, 1, 4, 3, 1, 2, 5, 4]
        point3f[] points = [(-1, 0, 0), (0, 0, 0), (1, 0, 0), (-1, 1, 0), (0, 1, 0), (1, 1, 0)]
        color3f[] primvars:displayColor = [(1, 0, 0), (0, 1, 0), (0, 0, 1), (1, 1, 0), (1, 1, 1), (1, 0, 1)] (
            interpolation = "varying"
        )
    }
}
```

```bash
$ usdchecker Mesh.usda
Success!
```

![color_mesh_400x205.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659949/c91ad2a1-d643-c0be-f1c3-35ac015d4f75.png)

### 面単位でデータアサイン

上の例では 6 個の頂点に色情報を割り当てましたが，面単位で区切って割り当てることもできます。はじめにひとつ目の面の 4 頂点，次にふたつ目の面の 4 頂点，… といった具合です。そのような指定は，Primvar の interpolation を **faceVarying** にすることで可能になります。

```Python
    # displayColor
    fvList = []
    fvList.append((1, 0, 0))  # Red
    fvList.append((0, 1, 0))  # Green
    fvList.append((1, 1, 1))  # White
    fvList.append((1, 1, 0))  # Yellow
    fvList.append((0, 1, 0))  # Green
    fvList.append((0, 0, 1))  # Blue
    fvList.append((1, 0, 1))  # Magenta
    fvList.append((1, 1, 1))  # White

    primvar_api = UsdGeom.PrimvarsAPI(mesh)
    primvar = primvar_api.CreatePrimvar('displayColor', Sdf.ValueTypeNames.Color3f, interpolation=UsdGeom.Tokens.faceVarying)
    primvar.GetAttr().Set(fvList)
```

このようにしても上と同じ結果が得られます。
この「面単位」でのアサインは，次の記事に書く予定のマテリアルの作成においてテクスチャマッピングで使用されることになります。


## 参考資料

ft-lab (Yutaka Yoshisaka) さん，USD-WG，あんどうめぐみさん による以下のサイトが理解の助けになりました。

https://github.com/ft-lab/Documents_USD/blob/master/doc/usd_create_mesh.md

https://github.com/usd-wg/assets/tree/main/docs/PrimvarInterpolation

https://fereria.github.io/reincarnation_tech/usd