Skip to content

オリジナルの New Schema Classを作る

USD のスキーマは自分で新しく定義することができます。
今回は、1からスキーマを作成してPython上から呼び出しをできるようにしてみます。

一応、公式のHPチュートリアルに
https://graphics.pixar.com/usd/docs/Generating-New-Schema-Classes.html

こちらがあります。
日本語ページは、
最近北口( @kit2cuz )さんの翻訳があります(感謝!!!!)ので
そちらも併せてご確認をば。

https://usd.prisms.xyz/tutorials/Generating-New-Schema-Classes.html

環境を作る

まずは環境を作ります。
プラグインスキーマを作りたい場合は、プラグインを単体で作ると言うより
USDのGitHubリポジトリからクローンしたプロジェクトをベースに作成していきます。

なぜならば、

公式のスキーマもカスタムスキーマもも扱いは同じ「プラグイン」なので
公式で用意されている各種ビルド環境を使う方が色々と容易にビルドすることができるからです。

というわけで、まずはUSDのビルドから始めます。

USDをビルドする

自分もなんとなく「USDのビルドってみんな面倒くさいって言ってるし・・・」と
ビルド方法すら、調べることすら拒否してるところがありましたが
実際は全然そんなことはなく、ものすごく簡単です。

まずは、
https://github.com/PixarAnimationStudios/USD
公式のリポジトリからソースコードをクローンしてきます。

そして、

  • Visual Studio2017
  • (おそらく) Windows 10 SDK
  • Python2.7

をインストールします。

次に、 Python2.7 の

  • C:/Python27
  • C:/Python27/Scripts

この2つのPATHを通します。

次に、 pip を使用して

1
2
pip install pyside
pip install PyOpenGL

この2つをインストールします。

そして最後に、

開発者コマンドプロンプト for VS 2017を開き、
クローンしたフォルダに移動して

1
python build_scripts\build_usd.py "<Build先>"

コマンドを実行します。
部分は、自分の好みのパスにします(例 I:/USD 等)

あとは1時間ほど待つと

ビルドが完了するので
あとは、書かれている通りに

PYTHONPATH を

  • USD/lib/python

に通すのと

PATHを

  • USD/bin
  • USD/lib
  • USD/plugin (これは後で説明)

この2つに通します。

以上で準備は完了です。

NewSchemaClassを作る

作業フォルダを作る

ここまで準備ができたら、NewSchemaClassを作成します。
まずは、USDの公式のビルドシステム(上の build_usd.pyを使用したやり方)に乗っかって
ビルドできるように、CMakeLists.txtを作成していきます。

リポジトリのプロジェクト下の

USD/extras/usd 下に、 custom というフォルダを作ります。
名前は何でもOKです。

次に、extras/usd 直下にある CMakeLists.txt に

1
2
3
add_subdirectory(examples)
add_subdirectory(tutorials)
add_subdirectory(custom)

add_subdirectory(####) #### は作成したフォルダ名 を追加します。
そして、customフォルダしたに作成するスキーマの作業フォルダを作り、
customフォルダ下に CMakeLists.txt を作成して

1
add_subdirectory(hogehoge)

add_subdirectory を追加します。

schema.usda を作る

フォルダができたら、次にカスタムスキーマのためのcppを作成する元になる
schema.usdaファイルを作成します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#usda 1.0
(
    subLayers = [
        @usd/schema.usda@
    ]
)

over "GLOBAL" (
    customData = {
        string libraryName       = "hogehoge"
        string libraryPath       = "."
    }
) { 
} 

class "FugaPrim" (
    inherits = </Typed>
    customData = {
        string className = "Fuga"
    }
)  {
    int intAttr = 0
}

ここで定義した内容が、カスタムスキーマの構造になります。

まず、必須なのが usbLayers = [~~~~]の部分と over "GLOBAL" 部分になります。
GLOBAL部分は、作成するプラグイン情報の定義を行う所で
libraryName が必須要素になります。
これは、全プラグイン(デフォルトのスキーマも含めて)でユニークである必要があります。

libraryPath は、ビルドした後の includeとlib 下のどこに Headerファイルを配置するか
指定する場所になります。

Info

現状、 libraryPath を入れると .h が見つからないエラーが出てビルドできない
それは原因調査中。
. の場合は include 直下に、ライブラリ名の Headerが作成されます

準備ができたところで、この schema.usda を cpp と .h ファイルにコンバートします。

1
usdGenSchema S:\fav\import_data\github\USD\extras\usd\custom\hogehoge\schema.usda

コンバートをするときは usdGenSchema に、作成した schema.usda を渡せばOKです。
Windowsのせいなのか、相対パスでは記述できないのだけ注意が必要です。

実行すると、同じフォルダに cpp と .h が作成されます。

必要なファイルを準備する

実行した cpp .h 以外にもいくつか必要なファイルがあるので
それらをサンプルからコピペしてきます。
USD/extras/usd/examples/usdSchemaExamples
場所はこのカスタムスキーマのサンプルから

  • module.cpp
  • moduleDeps.cpp
  • pch.h
  • init.py

この4つをコピペします。
そして少し書き換えます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include "Python.h"
#include "pxr/pxr.h"
#include "pxr/base/tf/pyModule.h"

PXR_NAMESPACE_USING_DIRECTIVE

TF_WRAP_MODULE
{
    TF_WRAP(HogehogeFuga);
}

まずは module.cpp の中の TF_WRAP_MODULEのなかに TF_WRAP( #### ) を作成したい
プロジェクト名+クラス名にします。
今回は hogehoge ライブラリの Fuga クラスだったので HogehogeFuga になります。
頭の文字は大文字に代わるので注意。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
TF_REGISTRY_FUNCTION(TfScriptModuleLoader) {
    // List of direct dependencies for this library.
    const std::vector<TfToken> reqs = {
        TfToken("sdf"),
        TfToken("tf"),
        TfToken("usd"),
        TfToken("vt")
    };
    TfScriptModuleLoader::GetInstance().
        RegisterLibrary(TfToken("hogehoge"), TfToken("pxr.Hogehoge"), reqs);
}

次が moduleDeps.cpp
このファイルは
上の TF_REGISTRY_FUNCTION 内にある RegisterLibrary にある TfToken("プロジェクト名"),TfToken("pxr.プロジェクト名") の部分を書き換えます。
pxr. のあとのプロジェクト名は頭を大文字にします。

1
2
3
import _hogehoge
from pxr import Tf
Tf.PrepareModule(_hogehoge, locals())

init.py は、ビルド後にPtyhon側でモジュールをロードしたときに呼び出すためのファイルです。
これは一番上にある import _#### 部分と、 PrepareModule の1つめの引数を _プロジェクト名に書き換えます。

pch.h は書き換えなくてもOKです。

CMakeLists.txt を作る

次に、作成したcppをビルドをするためのCMakeLists.txt を作成します。
hoge フォルダ下に CMakeLists.txt を作り、

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
set(PXR_PACKAGE hogehoge)

pxr_plugin(${PXR_PACKAGE}
    LIBRARIES
        tf
        sdf
        usd
        vt

    INCLUDE_DIRS
        ${Boost_INCLUDE_DIRS}
        ${PYTHON_INCLUDE_DIRS}

    PUBLIC_HEADERS
        api.h

    PUBLIC_CLASSES
        fuga
        tokens

    PYTHON_CPPFILES
        moduleDeps.cpp    

    PYMODULE_FILES
        __init__.py

    PYMODULE_CPPFILES
        module.cpp
        wrapFuga.cpp
        wrapTokens.cpp

    RESOURCE_FILES
        generatedSchema.usda
        plugInfo.json
        schema.usda:schema.usda
)

中身をこうします。

変更点は、1行目の PXR_PACKAGE 部分(ビルドされた後のlib等の名前がこれになります)
PUBLIC_CLASSES と PYMODULE_CPPFILES 部分で、ここに作成するクラス名(hoge)を入れます。
(指定したクラス名の頭が大文字の場合小文字になります)
Python用は、wrapが頭につきます。

以上で準備が完了です。

できあがったフォルダがこちら。

1
python build_scripts\build_usd.py "<Build先>"

準備ができたら、 build_usd.py を使用してビルドします。

ビルド結果を確認する

特にエラーがなく無事に完了すると、
ビルドフォルダとして指定したフォルダ下に、作成したlibとdll等が作成されます。

まず、 lib と dll。
この2つは USD/plugin/usd 下に作成されます。
このフォルダにできた2つを lib にコピーしても良いですが
カスタムプラグインは分けておいたほうが後々わかりやすいので
とりあえずこの USD/plugin/usd にPATHを通しておきます。

次が python/pxr/Hogehoge
これが、Pythonから呼び出すときのモジュールになります。

最後に include 下のHeaderファイル。
ここは、libraryPath下にプロジェクト名のフォルダが作成され
その下にHeaderファイルが作成されます。

こんなかんじ。

libraryPathが . 以外だとうまくいかないのが現状謎なので
今は直下にフォルダができます。

テストしてみる

とりあえず出来上がったモジュールをPython側から使ってみます。

1
2
3
4
5
6
#usda 1.0

def FugaPrim "Fuga"
{
    int intAttr = 100
}

とりあえず、こんな感じでサンプルのUSDファイルを作ります。

UsdViewで開くとこうなります。
ここの FugaPrimが schema.usda で指定したクラス名になります。

できあがったusda ファイルをPythonでロードしてみます。

1
2
3
4
5
6
from pxr import Usd, UsdGeom, Sdf, Gf
from pxr import Hogehoge

stg = Usd.Stage.Open("I:/test.usda")
cp = stage.GetPrimAtPath("/Fuga")
hoge.GetIntAttrAttr().Get()

ビルドが上手くいっていて libにちゃんとPATHが通っていれば
作成したモジュールが使えるようになっています。
ので、あとはファイルを開き、FugaPrimのプリムを取得してから
作成したカスタムスキーマ経由で値を取得します。

カスタムスキーマで定義した
int intAttr = 0
この部分が、 Get + 名前 + Attr().Get() という形でアクセスできてることが
わかるかと思います。

とりあえず第一段階はこれにて終了。

しかしながら、これだとPtyhon側から定義したスキーマのプリムの作り方がわからなかったので
そのあとはまた次回。