Skip to content

PrimをReparentする

USD の親子構造をあとから編集したい...
みたいなことは当然のことながらやりたくなります。
しかしドキュメントを読み漁ってもみつからずうわーんになっていたので泣きついて
やり方を教えてもらったのでメモをば。

準備

まずはかんたんな usda ファイルを用意します。

1
2
3
4
5
6
#usda 1.0

def "A"{
    def "B"{}
    def "C"{}
}

これを

1
2
3
4
5
6
7
#usda 1.0

def "A"{
    def "B"{
        def "C"{}
    }
}

こう。

図にするとこんな感じにしたいとします。

基本的な移動方法

Maya などの場合は cmds.parent(子,親) などでノードの移動ができますが
USD の場合は若干異なります。
どう違うかというと、Prim や Layer オブジェクトで行うのではなく
SdfBatchNamespaceEditと呼ばれる、Namespace(あとで説明)の操作をカプセル化したクラスを介して
Prim の移動(Reparent)を行います。

1
2
3
4
5
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 の階層を編集することができます。

1
2
edit.Add("/A/C", "/A/B/C")
edit.Add("/A/D", "/A/B/D")

Batch なので複数の操作を書くことができます。

1
2
[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 関係の操作を実行することができて

1
2
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 の定義があるレイヤーをサブレイヤーでロードした

1
2
3
4
5
6
#usda 1.0
(
    subLayers = [
        @namespaceEdit.usda@
    ]
)

こんなファイルを作成して、このレイヤーに対して Apply(BatchNamespaceEdit) を実行したとしても
False になってしまい正しく実行できません。

なので、サブレイヤーをしている場合は

1
2
3
# Primの定義があるレイヤーを取得して、そのレイヤーに対してApply
stack = stage.GetLayerStack()[2]
stack.Apply(edit)

定義があるレイヤーを取得して、そのターゲットに対して Apply します。

サブレイヤーで読み込んだ先に定義がある場合

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#usda 1.0

(
    subLayers = [
        @namespaceEdit.usda@
    ]
)

over "A"{
    over "B"{}
    over "C"{}
    over "D"{}
}

定義がない場合はなにもおきないですが、
例えばこんな感じでサブレイヤーに対して Prim が定義されている場合
EditNamespace をするとどうなるかというと

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
def "A"
{
    def "B"
    {
        over "C_rename"
        {
        }
    }

    def "C"
    {
    }

    def "D"
    {
    }
}

こうなりました。
あくまでも編集ターゲットにしたレイヤーの定義のみ変更されます。

なので、これだと具合が悪い(コンポジションした結果に対して Prim 移動をしたい)場合は

1
2
flatten = stage.Flatten()
flatten.Apply(edit)

Flatten してから Edit するか

1
2
for layer in stage.GetLayerStack():
    layer.Apply(edit)

LayerStack に対して edit するとかでもいけそうです。

しかし、Prim 移動を行うのが BatchNamespaceEdit というのはさすがにわかるかあぁぁぁ!!!!
になりましたよ...

参考