PrimをReparentする
USD の親子構造をあとから編集したい...
みたいなことは当然のことながらやりたくなります。
しかしドキュメントを読み漁ってもみつからずうわーんになっていたので泣きついて
やり方を教えてもらったのでメモをば。
準備
まずはかんたんな usda ファイルを用意します。
#usda 1.0
def "A"{
def "B"{}
def "C"{}
}
これを
#usda 1.0
def "A"{
def "B"{
def "C"{}
}
}
こう。
図にするとこんな感じにしたいとします。
基本的な移動方法
Maya などの場合は cmds.parent(子,親) などでノードの移動ができますが
USD の場合は若干異なります。
どう違うかというと、Prim や Layer オブジェクトで行うのではなく
SdfBatchNamespaceEditと呼ばれる、Namespace(あとで説明)の操作をカプセル化したクラスを介して
Prim の移動(Reparent)を行います。
stage = Usd.Stage.Open(r"D:\work\usd_py36\usd\namespaceEdit.usda")
layer = stage.GetRootLayer()
edit = Sdf.BatchNamespaceEdit()
edit.Add("/A/C", "/A/B/C")
layer.Apply(edit)
BatchNamespaceEdit は、「Batch」と名のつくとおり移動処理を複数積み上げてから
編集したいレイヤーに対して Apply することで、Prim の階層を編集することができます。
edit.Add("/A/C", "/A/B/C")
edit.Add("/A/D", "/A/B/D")
Batch なので複数の操作を書くことができます。
[Sdf.NamespaceEdit(Sdf.Path('/A/C'),Sdf.Path('/A/B/C'),-1),
Sdf.NamespaceEdit(Sdf.Path('/A/D'),Sdf.Path('/A/B/D'),-1)]
どんな操作が積まれているかは、Python の場合は edits で確認できます。(C++とは違う)
Prim 操作を探しているときに NamespaceEdit は見つけていたのですが
うまく行かないしそもそもターゲット指定していないのにどうやってつかうんだ...
と思っていたのですが、Batch に渡すための1つの操作が NamespaceEdit でした。
ひどいトラップ...
Namespace とは
USD の Namespace とは Glossaryによると
Namespace is simply the term USD uses to describe the set of prim paths that provide the identities for prims on a Stage, or PrimSpecs in a Layer.
木構造内のある Prim (レイヤーの場合は PrimSpec)を識別するのに使うものです。
(A という Namespace にある B、等)
つまりは、上の BatchNamespaceEdit というのは、
Namespace つまりはステージまたはレイヤーの階層構造を編集するためのものだよーということです。
なお、この Namespace の位置を表すのが SdfPath になります。
移動以外の操作
BatchNamespaceEdit では、Prim の移動以外にも Namespace 関係の操作を実行することができて
edit.Add("/A/B/D", Sdf.Path.emptyPath) # 削除
edit.Add("/A/B/C","/A/B/C_rename")
こうすると、Prim の削除やリネームを行うことができます。 これ以外にも Variant の Reparent やプロパティのリネームなどもできるので シーングラフやプロパティの構造を大きく変更したい場合は、この方法を使うと できることが増えそうです。
SubLayer 時の Namespace 操作
注意点ですが、この BatchNamespaceEdit を実行するのが SdfLayer だということ。
なので、Prim の定義があるレイヤーをサブレイヤーでロードした
#usda 1.0
(
subLayers = [
@namespaceEdit.usda@
]
)
こんなファイルを作成して、このレイヤーに対して Apply(BatchNamespaceEdit) を実行したとしても
False になってしまい正しく実行できません。
なので、サブレイヤーをしている場合は
# Primの定義があるレイヤーを取得して、そのレイヤーに対してApply
stack = stage.GetLayerStack()[2]
stack.Apply(edit)
定義があるレイヤーを取得して、そのターゲットに対して Apply します。
サブレイヤーで読み込んだ先に定義がある場合
#usda 1.0
(
subLayers = [
@namespaceEdit.usda@
]
)
over "A"{
over "B"{}
over "C"{}
over "D"{}
}
定義がない場合はなにもおきないですが、
例えばこんな感じでサブレイヤーに対して Prim が定義されている場合
EditNamespace をするとどうなるかというと
def "A"
{
def "B"
{
over "C_rename"
{
}
}
def "C"
{
}
def "D"
{
}
}
こうなりました。
あくまでも編集ターゲットにしたレイヤーの定義のみ変更されます。
なので、これだと具合が悪い(コンポジションした結果に対して Prim 移動をしたい)場合は
flatten = stage.Flatten()
flatten.Apply(edit)
Flatten してから Edit するか
for layer in stage.GetLayerStack():
layer.Apply(edit)
LayerStack に対して edit するとかでもいけそうです。
しかし、Prim 移動を行うのが BatchNamespaceEdit というのはさすがにわかるかあぁぁぁ!!!! になりましたよ...