メインコンテンツまでスキップ

UsdCollectionを使おう

USD の「Collection」とは、Maya の Sets と似た機能で USD シーン内のオブジェクトを識別するためのリレーションシップ機能を提供します。 つまりは、これをつかうと Prim をグループでまとめるだけではなく Namespace に関係なくオブジェクトをコントロールできるようになります。

やってみる

まずはサンプルを用意します。

このようなシンプルなレイヤーを用意します。

# Collectionを作る
collection = stage.DefinePrim("/collectionSample")
collectionName = "sampleCollection"
api = Usd.CollectionAPI.Apply(collection,collectionName)

そして、Collection を作成したい Prim に UsdCollectionAPI を利用して Collection を適応します。

#usda 1.0

def "collectionSample" (
prepend apiSchemas = ["CollectionAPI:sampleCollection"]
)
{
}

適応できました。 ですが、これだけだとまだからの Set を用意したにすぎないので Prim を指定してみます。

api.IncludePath(Sdf.Path("/World/SamplePrims/Cube"))
#usda 1.0

def "collectionSample" (
prepend apiSchemas = ["CollectionAPI:sampleCollection"]
)
{
prepend rel collection:sampleCollection:includes = </World/SamplePrims/Cube>
}

追加されました。 指定した Prim へのリレーションに、IncludePath が追加されたのがわかります。

Collection を追加すると、追加した Prim に対して「collection」の Namespace 以下に プロパティが作成されます。 そして、このプロパティ名には Collection 名(上の例なら sampleCollection )が付き そのコレクションごとのプロパティを追加するための Namespace が提供されます。 プロパティ以下に Collection 名が入ることで、 1つの Prim に対して複数の Collection が追加できるようになるわけですね。

Prim の取得

Collection に入れた Prim の取得方法

print(api.GetIncludesRel().GetTargets())

IncludePath の指定は、リレーションで定義されているので、 CollectionAPI の GetIncludesRel でリレーションを取得すると かんたんに Collection に含まれている Prim を取得することができます。

ExcludePath と ExpansionRule

上の例だと、IncludePath を指定することで Collection に Prim を入れることができました。 ですが、IncludePath として指定した Prim だけではなく その子 Prim も含めて取得したい場合もありますし、 その子 Prim のうち「除外したい Prim」も指定したいことがあるかとおもいます。

そうした時は ExcludePath(除外指定)と、ExpansionRule(どのように展開するか) を指定した上で、ComputeMembershipQuery を使うことで Collection に含まれる Prim を取得することができます。

api.ExcludePath(Sdf.Path("/World/SamplePrims/Cube/ExcludeCube"))

まずは、サンプルのレイヤーのうち、Cube/ExcludeCube 以下を除外するようにします。

expansionRule = api.CreateExpansionRuleAttr()
expansionRule.Set("expandPrims")

そして ExpansionRule を指定します。 これが、指定 Prim 以下の子すべてなのかだったり、その Prim のみ指定だったりといった 選択をどのように展開するかを指定するアトリビュートになります。

name
explicitOnlyIncludePath のみで子 Prim は含めない
expandPrimsIncludePath 以下の子 Prim も含める
expandPrimsAndPropertiesIncludepath 以下の子 Prim とアトリビュートも含める

選択肢はこの3つです。

次に、この Collection を利用して Prim を取得します。

query = api.ComputeMembershipQuery()
# expandPrimsで指定されてるので、IncludePath以下にありExcludeに含まれないPrimがリストされる
print(Usd.CollectionAPI.ComputeIncludedObjects(query,stage))

api から ComputeMembershipQuery で、検査くるための Query を取得します。 そして、それを利用して ComputeIncludeObjects を実行すると 引数で指定した stage 内のうち、Collection に該当する Prim を取得できます。

[Usd.Prim(</World/SamplePrims/Cube>), Usd.Prim(</World/SamplePrims/Cube/pCube1>), Usd.Prim(</World/SamplePrims/Cube/pCube3>)]

結果、ExcludePath で指定した ExcludeCube と pCube2 の Prim は除外され それ以外の結果が取得できました。

expansionRule.Set("explicitOnly")

これを explicitOnly に変えると、

[Usd.Prim(</World/SamplePrims/Cube>)]

子以下は含まれなくなりました。

expansionRule.Set("expandPrimsAndProperties")

最後に、Properties まで展開するとどうなるかというと Attribute までふくめてすべて取得することができました。

この ExpansionRule は、MembershipQuery の

print(query.GetAsPathExpansionRuleMap())

ExpansionRuleMap で確認することができて、この RuleMap に従って ステージ内の Prim を「コレクション」することができます。

Path が Collection に含まれるかどうか調べる

以上のやりかたで Collection に含む Prim を取得することができましたが ある SdfPath が Collection に含まれているかを知りたい...といったケースも 発生するかとおもいます。

その時は、ComputeMembershipQuery を利用して

# 引数で指定したSdfPathがCollectionに含まれるか
print(query.IsPathIncluded("/World/SamplePrims/Cube/pCube1"))
# 除外されたPrimの場合
print(query.IsPathIncluded("/World/SamplePrims/Cube/ExcludeCube/pCube2"))

このように Path が含まれるかをチェックすることができます。

Houdini

コードと usda だけだとわかりにくいので、同様のことを HoudiniSOLARIS で試してみます。

LOP には Collection ノードが存在しているので、ノードを作成します。

DefaultPrimitivePath は、Collection を追加したい Path を指定します。 そしてその下に Collection に入れる条件を指定します。

上の例だと、青枠を Collection に入れて、そのうち赤枠部分は除外 ExpansionRule は Cube 以下の子 Prim 全てを入れるようにしました。

Collection が作れているかを Prune ノードを使用して確認してみます。 Prune は、 PrimitivePattern で指定した Prim を非表示状態にします。

PrimitivePattern には Collection を指定することもできるので、 カーソルボタンを押して Collection を指定すると

結果。 Collection に含まれている pCube2 以外の Prim が非表示になりました。

最初に説明したとおり、IncludePath と ExcludePath、そして ExpansionRule によって Collection に含まれる Prim が計算され その結果によって Prune ノードが実行されました。 Prune にも同じような TargetPrimitives の指定がある(Collection を作る機能もある)のですが 挙動・指定方法は、USD の Collection と同様になります。

まとめ

Collection を使用すれば、IncludePath/ExcludePath/ExpansionRule を組み合わせることで 単純なリレーションよりも、より手軽にステージ内の Prim にアクセスできるように なることがわかりました。

これを使って、指定の Prim に自動で処理をさせたり 自分のやりたいシーングラフを表現できるようになるので、よりいっそう USD で できることの幅が広がりそうです。

https://fereria.github.io/reincarnation_tech/60_JupyterNotebook/USD/USDCollectoinSample2 Python での操作方法は ↑ の Notebook で色々試してるので参考にしてください。