mergeMAXFileで合成したノード一覧を取得する

投稿者: | 2017/07/24

mergeMAXFileは現在のシーンにMaxファイルを合成する関数ですが、残念ながら合成したノードを戻り値として返してくれません。

代わりに#selectオプションを指定する事で合成したノードを選択状態に出来るのですが、選択されるノードはファイル内で表示状態になっているノードだけです。つまり、元のファイル内で非表示になっているノード、例えばリグのブラックボックス等は選択状態になりません。

それだと読み込んだノードを処理する場合に非常に困ってしまいますので、今回はそういったノードを全て取得する方法について考えてみます。

参照: リファレンス – 3ds Max ファイルのロードと保存

シーン内のノードと差分を取る

最も簡単な方法として、シーン内のノードを予め配列などに格納しておき、マージ後に差分を取る方法があります。

しかし少し考えてみても分かるとおり、この方法は非常に処理コストが大きくなってしまいます。特にシーンファイルが大きかった場合、処理が返ってこなくなる程重くなる可能性があります。

そこで少し工夫して、全てのノードの差分を取るのではなく、代わりにルートノードのみ差分を取る事にします。
こうすれば処理コストは非常に小さく出来ます。ただし、制限として#neverReparentオプションが必須になります。

加えてノードの差分を配列ではなく、ハッシュテーブルを使って取る事で処理速度を高速化します。

-- Maxファイルをシーンに合成する
-- Return: 合成したルートノード一覧
fn mergeMaxFileEx filename =
(
    local rootHt = dotNetObject "System.Collections.Hashtable"
    local addItem = rootHt.Add
    local hasItem = rootHt.Contains
    for c in rootNode.children do
        addItem c.inode.handle undefined

    -- Memo: #neverReparent必須
    local res = mergeMaxFile filename #neverReparent quiet:true 
    if not res do return undefined

    for c in rootNode.children where 
        not (hasItem c.inode.handle) collect c
)

関数 mergeMaxFileEx は引数にMaxファイルパスを取り、マージ後にトップレベルノードを戻り値として返します。

例えば以下のような構造のMaxファイルがあったとします。

-- C:/workspace/test.max
$rootObj001
    $obj001
        $obj002
$rootObj002
    $obj003

この場合、以下のような戻り値を返します。

mergeMaxFileEx @"C:/workspace/test.max"

>> #($rootObj001, $rootObj002)

このままだとルートノードしか取得出来ないので、全てのノードが必要な時には以下のようにします。

-- ツリー内にある全てのノードを取得する
fn getTreeNodes root _ary:#() =
(
    append _ary root
    for c in root.children do
        getTreeNodes c _ary:_ary
    _ary
)

local rootObjs = mergeMaxFileEx @"C:/workspace/test.max"
local mergedObjs = #()
for obj in rootObjs do
    join mergedObjs (getTreeNodes obj)
>>mergedObjs
#($rootObj001, $obj001, $obj002, $rootObj002, $obj003)

これらの関数は1つにまとめてもいいのですが、今回は機能を分離する為に個別の関数としました。

一つのことをうまくやれとも言いますしね。

コメントを残す

メールアドレスが公開されることはありません。