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つにまとめてもいいのですが、今回は機能を分離する為に個別の関数としました。
一つのことをうまくやれとも言いますしね。