👻

USDで作成したUSDファイルをMacbookのプレビューソフトで表示してみた

に公開

今回はUSD形式で作成したファイルをMacbookのプレビューソフトで開くことができることを紹介しようと思います。

USDとは?

USDは3Dシーンを共同で構築するための高性能かつ拡張可能なソフトウェアプラットフォームであり、大規模な開発ニーズを満たせるように構築されています。USDではジオメトリを初めシェーディングやライティング、物理など様々な必須領域をカバーするように構築されています。USDはピクサーによって最初に開発されたものとなり、特にオープンソースを意識したものとしてOpenUSDが位置付けられているようです。

https://openusd.org/release/index.html

https://ja.wikipedia.org/wiki/Universal_Scene_Description

USDの保存形式

USDファイルはいくつかの保存形式があり、それぞれ用途が異なります。

  • .usda: ASCIIにより人間により読み書き可能な形式で保存
  • .usdc: 圧縮することにより読み取りを高速にしたファイル形式
  • .usd: .usdaまたは.usdcどちらの形式も受け付ける
  • .usdz: 非圧縮でアーカイブされた形式であり、完成して今後編集される予定がないようなリソースを取り扱う時に利用されることが多い

今回は.usdaファイルを利用しようと思います。後ほどコードをお見せしますが、指定する拡張子を変更するだけでそれに合わせた保存がされます。

今回の実験内容

今回はUSDファイルをMacbook標準搭載のプレビューソフトを利用して開くまでの一連の流れを共有します。なお、USDファイルはBlenderをはじめとした様々な3Dグラフィックスソフトウェアで対応されているので、それらのソフトに慣れている方はあえてプレビューを利用しなくても問題ありません。

環境構築

PythonでUSDを扱うにはusd-coreというライブラリを利用するのが一般的です。以下のように環境構築します。

uv init usd_preview -p 3.12
cd usd_preview
uv add usd-core

コードの実装

今回はCube(いわゆる立方体)とSphere(いわゆる球体)の二つを指定した座標におくコードを実装してみます。

create_usd.py
from pxr import Usd, UsdGeom, Gf


stage = Usd.Stage.CreateNew("geometries.usda")

world = UsdGeom.Xform.Define(stage, "/World")
cube = UsdGeom.Cube.Define(stage, world.GetPath().AppendPath("Cube"))
sphere = UsdGeom.Sphere.Define(stage, world.GetPath().AppendPath("Sphere"))

UsdGeom.XformCommonAPI(cube.GetPrim()).SetTranslate(
    Gf.Vec3d(2.0, 0, 0)
)
UsdGeom.XformCommonAPI(sphere.GetPrim()).SetTranslate(
    Gf.Vec3d(-2.0, 0, 0)
)
stage.Save()

まず、pxr.XXXという機能をimportすることで利用できるようになります。

USDを編集するには最初にステージを作成します。今回は新たにUSDファイルを作成するので以下のようにUsd.Stage.CreateNewを実行しており、ファイルの拡張子はusdaにしています。

stage = Usd.Stage.CreateNew("geometries.usda")

次にいわゆる座標系(/World)を作成し、その直下にCubeとSphereを作成します。なお、USDでは階層構造でリソースは管理されます。

world = UsdGeom.Xform.Define(stage, "/World")
cube = UsdGeom.Cube.Define(stage, world.GetPath().AppendPath("Cube"))
sphere = UsdGeom.Sphere.Define(stage, world.GetPath().AppendPath("Sphere"))

このままでもCubeとSphereは表示できますが同じ座標に作成して被ってしまうので、座標をそれぞれ指定してあげます。座標変換などの処理はUsdGeom.XformCommonAPIを利用することが一般的であり、CubeとSphereに対してそれぞれ座標を割り当てています。

UsdGeom.XformCommonAPI(cube.GetPrim()).SetTranslate(
    Gf.Vec3d(2.0, 0, 0)
)
UsdGeom.XformCommonAPI(sphere.GetPrim()).SetTranslate(
    Gf.Vec3d(-2.0, 0, 0)
)

最後に、ここまで設定した内容をstage.Save()を実行することでファイルに保存します。

このコードを実行すると以下のようなgeometries.usdaファイルが作成されます。先ほど説明したようにUSDでは階層構造が採用されており、Worldがトップにあり、その下にCubeとSphereがあることが確認できます。

geometries.usda
#usda 1.0

def Xform "World"
{
    def Cube "Cube"
    {
        double3 xformOp:translate = (2, 0, 0)
        uniform token[] xformOpOrder = ["xformOp:translate"]
    }

    def Sphere "Sphere"
    {
        double3 xformOp:translate = (-2, 0, 0)
        uniform token[] xformOpOrder = ["xformOp:translate"]
    }
}

プレビューで開いてみる

早速プレビューで開いてみましょう。作成したgeometries.usdaをプレビューで開くと以下のような表示がされるかと思います。なお、マウス操作すると視点を色々と変更することができます。

試しに色もつけてみる

先ほどの例では色は特に指定してなかったですが、CubeとSphereそれぞれに色をつける場合以下のようにすると実施できます。

create_usd_with_color.py
from pxr import Usd, UsdGeom, Gf, Sdf


stage = Usd.Stage.CreateNew("geometries_with_color.usda")
world = UsdGeom.Xform.Define(stage, "/World")

cube = UsdGeom.Cube.Define(stage, world.GetPath().AppendPath("Cube"))
sphere = UsdGeom.Sphere.Define(stage, world.GetPath().AppendPath("Sphere"))

UsdGeom.XformCommonAPI(cube.GetPrim()).SetTranslate(
    Gf.Vec3d(2.0, 0, 0)
)
UsdGeom.XformCommonAPI(sphere.GetPrim()).SetTranslate(
    Gf.Vec3d(-2.0, 0, 0)
)

cube_primvars = UsdGeom.PrimvarsAPI(cube.GetPrim())
cube_primvars.CreatePrimvar(
    "displayColor",
    Sdf.ValueTypeNames.Color3fArray
)
cube_color_primvar = cube_primvars.GetPrimvar("displayColor")
cube_color_primvar.Set([Gf.Vec3f(0.0, 1.0, 0.0)])

sphere_primvars = UsdGeom.PrimvarsAPI(sphere.GetPrim())
sphere_primvars.CreatePrimvar(
    "displayColor",
    Sdf.ValueTypeNames.Color3fArray
)
sphere_color_primvar = sphere_primvars.GetPrimvar("displayColor")
sphere_color_primvar.Set([Gf.Vec3f(1.0, 0.0, 0.0)])

stage.Save()

詳しい説明は省略しますが、以下のようにUsdGeom.PrimvarsAPIで属性を定義しSetを利用して値を設定します。色はRGBの順に指定するので、Cubeは緑、Sphereは赤が設定されます。

geometries_with_color.usda
#usda 1.0

def Xform "World"
{
    def Cube "Cube"
    {
        color3f[] primvars:displayColor = [(0, 1, 0)]
        double3 xformOp:translate = (2, 0, 0)
        uniform token[] xformOpOrder = ["xformOp:translate"]
    }

    def Sphere "Sphere"
    {
        color3f[] primvars:displayColor = [(1, 0, 0)]
        double3 xformOp:translate = (-2, 0, 0)
        uniform token[] xformOpOrder = ["xformOp:translate"]
    }
}

まとめ

今回はUSDをMacbookのプレビューソフトで開いてみました。今までは作成したUSDファイルをBlenderにインポート指定見ていたのですが、プレビューソフトでも結果が確認できるのを知ってから一気に作業効率が上がりました。大規模なファイルになるとロードに時間がかかったりするのかもしれませんが、私が開発している範囲ではまだ問題になっていないのでどんどん使っていこうと思います。また、今回はUSDのプログラミングについてはあまり詳しく掘り下げなかったですが、今後どんどん掘り下げていければと思っています。

Discussion