Skip to content

AssetResolution(1) - usdResolverExample

Universal Scene Description AdventCalendar2021 15 日目は、
USD の Asset Resolution (アセットパスの解決)についてです。

AssetResolution とは

AssetResolution とは、その名の通りアセットの実際のリソースまでのパスを
解決するためのプロセスのことを指します。

Stage/Layer/Spec の記事に書いた通り、USD は1つのファイルではなくて複数の(それは多くの)レイヤーによって
1つのステージが構成されています。
つまり、あるレイヤーの中には別のレイヤーへのパスが記述されているはずです。

例えば、Kitchen_set.usd を見ると、中で Reference されているモデルは ./assets/~ といった
現在開いているファイルからの相対パスで記述されていて、
子の相対パスで記述されたアセットのパスを「現在のファイルから見た相対パス」として
探し出し、その結果をロードしています。
あるいは、 PXR_AR_DEFAULT_SEARCH_PATH という環境変数が指定されている場合は
Windows の PATH と同じように、DEFAULT_SEARCH_PATH で指定されたパス以下を検索して
見つかった場合は、そのフォルダ以下にあるファイルを使用するようになっています。

図に表すと、このようになります。

デフォルトの場合はその名の通り「ArDefaultResolver」と呼ばれるアセットのパス解決のための
機能が実装されています。
この ArDefaultResolver は、相対パスで記述されたアセットのパスを検索し
実際のリソースまでのフルパスを返します。
これによって、相対パスで記述されていてもフルパスで表示ができるようになっています。

このように、ある入力のパス(相対パス)からリソースまでのフルパスを検索して
その結果を返す機構のことを「Asset Resolution」と呼び、
そのためのインターフェースを AssetResolver と呼びます。
デフォルトの ArDefaultResolver も ArAssetResolver から継承され
実装されたプラグインです。

このパスを解決するためのプロセスは、独自にカスタマイズすることが可能です。

具体的な使い方を見る

といっても、これだけでは使用イメージがつかみにくいと思いますので
今回は、USD リポジトリ以下にあるサンプルプラグイン usdResolverExample をみながら
どのようなものか、そしてどう使うのかを説明していこうとおもいます。

ビルドする

まずは USD をビルドします。

1
git clone https://github.com/PixarAnimationStudios/USD.git

どこか適当なフォルダにクローンしておきます。
そして、

環境変数の PATH に、Python の Path を通しておきます。
Python のバージョンは、3.7.9あたりを入れておきます。
なお私は、3.7.7 ですが多分それでも大丈夫でしょう。

VisualStudio2017 を事前にインストールしておいて、開発者コマンドプロンプト for VS 2017 をクリックします。

1
pip install PyOpenGL PySide2

忘れずに pip で PyOpenGL と PySide2 を入れておきましょう。

1
python build_scripts/build_usd.py <インストールするフォルダ>

あとは、これを実行して30分ほど待ちます。
Python は、環境変数で指定されている PATH の Python でビルドされるので
今回なら 3.7.7 で使用できる USD ができあがります。

はじまって

無事終わると、このような表示になります。

ビルドが完了したら、インストール先のパスを環境変数に指定します。
しかし困ったことに、この環境変数を指定してしまうと Houdini がいろいろおかしなことになってしまうので

1
2
3
set PATH=C:\USD\lib;C:\USD\bin;%PATH%
SET PYTHONPATH=C:\USD\lib\python;%PYTHONPATH%
cmd /k

私はこのような Bat を用意してそこから起動するようにしています。
(あるいは VSCode の環境変数で指定)

サンプルプラグインをコピーする

ビルドができたら、usdResolverExample を plugin にコピーします。
ビルドで指定したフォルダ(サンプルの場合 C:/USD)以下 share/usd/examples/plugin
にある

この3つのファイルを、 plugin/usd 以下にコピーします。

コピーができたら準備完了です。

サンプルを開く

サンプルの Resolver が使えるようになったので、
Resolver のサンプル usd ファイルを見ながらどのようなものかを見ていきましょう。

extras/usd/examples/usdResolverExample/testenv/testUsdResolverExample

サンプルは、以下のフォルダにあるので
このフォルダをどこかわかりやすいところにコピーします。
自分の場合は D ドライブ直下に置いておきます。

まずは何も考えず、サンプルの
shot_1/shot_1.usda
を開いて見ます。

すると、盛大にエラーが出ていて表示できません。

shot_1.usda をエディタで開いてみると

1
references = @asset:Buzz/Buzz.usd@

リファレンスで読み込むファイルが asset: から始まるパスになっていて、相対パスではありません。
この「asset」は、URI(Uniform Resource Identifiers) と呼ばれる
通常の相対パスなどではなく特定の処理をするための「識別子」になっています。

asset: と uriSchemes

この識別子は、pluginfo.json によって指定されています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
{
    "Plugins": [
        {
            "Info": {
                "Types": {
                    "UsdResolverExampleResolver": {
                        "bases": ["ArResolver"],
                        "uriSchemes": ["asset"],
                        "implementsContexts": true
                    }
                }
            },
            "LibraryPath": "../usdResolverExample.dll",
            "Name": "usdResolverExample",
            "ResourcePath": "resources",
            "Root": "..",
            "Type": "library"
        }
    ]
}

usdResolverExample の pluginfo.json を見ると、
Types の uriSchemes に asset が指定されているのがわかります。
これは、
アセットパスに asset:~~ になってい場合は、UsdResolverExampleResolver プラグインを呼び出すようにしてくれ
という、AssetResolver プラグインを使用するための設定になっています。

usd の plugins は、plugin/usd/pluginfo.json にある内容をロードします。
そして、 plugin/usd/plugin/pluginfo.json は subdir/resources/pluginfo.json を
Include します。
なので、今回の Resolver プラグインの pluginfo.json は plugin/usd/usdResolverExample/resources 下にあります。
このあたりの話はまたいずれ。

1
set USD_RESOLVER_EXAMPLE_ASSET_DIR=D:\testUsdResolverExample\assets

サンプルの Resolver は、アセット置き場を別途指定してあげる必要があるので
上のように USD_RESOLVER_EXAMPLE_ASSET_DIR を指定して
もう一度 shot_1/shot_1.usda を開いてみます。

今度は開くことができました。

usdview で Buzz Prim のコンポジションをみると、 assets 以下の Buzz がリファレンスされているのがわかります。
usdResolverExample プラグインは、
asset:Buzz の Buzz 部分が AssetName として解釈して、USD_RESOLVER_EXAMPLE_ASSET_DIR 以下の AssetName の AssetName.usd
にアセットパスが解決されました。

Asset のフォルダ構成

ではこの呼び出されている Buzz アセットフォルダ以下はどのような構成になっているでしょうか。

Buzz 以下に Buzz.usd があり、
サブフォルダでバージョンごとのフォルダがあるのがわかります。

Buzz 以下にある Buzz.usd を usdcat してみると

Buzz/Buzz.usd は、
Buzz 以下 のバージョンフォルダをペイロードしています。

アセットは、このようにバージョン管理をすることがよくあると思いますが、

asset:Buzz/Buzz.usd

asset:\<AssetName> のように解釈するのに加えて、

1
2
3
4
{
    "Buzz" : "1",
    "Woody" : "1"
}

shot_1 以下にある versions.json というファイルの中身を参照し、
このファイルに書かれているバージョンのアセットを使用するようになっています。

つまりまとめると、アセットのパスが asset:\<AssetName>/\<AssetName>.usd のように指定されていた場合、

  1. shot 以下の versions.json を見て、環境変数 VESION に AssetName のバージョンを指定
  2. USD_RESOLVER_EXAMPLE_ASSET_DIR で指定したアセットフォルダ以下から
  3. \<AssetName> 以下の AssetName.usd をリファレンスして
  4. その本体は、\<AssetName> : version で指定されているバージョン以下のものをペイロードする

ということが、 usdResolverExample で行われています。

試しに、 versions.json を

1
2
3
4
{
    "Buzz" : "latest",
    "Woody" : "1"
}

このようにかきかえてみます。

すると、AssetInfo は latest ではなく 2 になっています。

次は バージョンフォルダを増やしてみます。

latest をコピーして 2 にします。
latest 以下の Buff_sublayer.usd の AssetInfo を書き換えて version:3 にしておきます。

すると、latest 以下の Buzz.usd が読まれているのがわかります。

1
2
3
4
{
    "Buzz" : "2",
    "Woody" : "1"
}

2 にすれば、 latest ではなく 2 のフォルダを参照しに行きます。

Resolver の効果について

以上の検証から、AssetResolver を使用することで以下のような効果が得られることがわかります。

  1. 相対パスではなく、アセット名などを使用して実際のリソースへのパス解決を作れる
  2. バージョン管理を USD のプラグイン側でコントロールできる
  3. アセットのバージョンを別ファイルに記述して固定化できる

これらができることによってなにが変わるかというと、
アセットの管理側(ShotGrid のようなデータベース)とデータ側(USD)とのプラグインレベルでの連携が可能になり、
OS のファイルシステムのパスに依存しないアセットのパス管理ができるようになります。

たとえば、SQL や S3 を使用した AssetResolver のサンプルなどもすでに存在しています。(Resolver2.0 だと動かない)
このサンプルの場合、 sql://~~ のようにアセットパスを指定することで、
アセットのパスをデータベースを使用して解決しています。

usdResolverExample は、version を json で保存していましたが
例えばこの部分を ShotGrid と連携させて、ShotGrid のバージョン管理と連動させることで
Shot でどのバージョンのモデルを使うかを管理したり
ファイルの置き場所を ShotGrid あるいはデータベースにて管理したりといったことが
可能になります。

USD に書かれているアセットパスは asset:Name/Name.usd (もちろん変えられる)なので、
よくありがちな、ファイルの置き場所が変わったらリファレンスやテクスチャのパスが切れて
どうにもならなくなる...だったり、
フォルダ階層で管理しようとしてものすごく階層が深くなり、人間にはどうにもならない感じになったり
同じファイルが複数の場所に散らばってカオスになったり。
環境が変わったら(以下略
といった無数の悲劇を回避することができます。

まとめ

AssetResolution そして、AssetResolver がどんなものでどういうことができるかつかめたでしょうか。

USD/extras/usd/examples/usdResolverExample

今回見てきた usdResolverExample のソースコードは上記のフォルダにあるので
実際にどのように実装すればよいかはサンプルを見るとよくわかります。

多数の USD ファイル(レイヤー)によって構成されている USD だからこそ、
リソースへのパス解決は重要になりますので
パイプラインを設計するときに、アセットのパスをどのように管理するかによって
カスタムの AssetResolver を実装すると、より USD を便利に扱えるようになると思います。

次回は、この AssetResolution を踏まえて
USD ツールのうちのひとつ usdresolve と、Python でのパス解決について
書く予定です。