MaxScriptTip: 特定のマテリアル下にある全てのサブ要素を取得する

9月 23, 2016
今回はマテリアルのサブ要素(マテリアル、テクスチャ)の取得について。

マテリアルは要素の下に別の要素がツリー状に接続される、いわゆる階層構造を持っています。
こういった構造では、再帰処理による取得が強力です。

以下は指定マテリアル下の全てのサブ要素を取得する関数です。
fn getAllSubMtlAndMaps mtl subs:#() =
(
    if mtl != undefined and (appendIfUnique subs mtl) do
    (
        if (superclassof mtl) == material do
        (
            for i = 1 to (getNumSubMtls mtl) do
                getAllSubMtlAndMaps (getSubMtl mtl i) subs:subs
        )
  
        for i = 1 to (getNumSubTexmaps mtl) do
            getAllSubMtlAndMaps (getSubTexmap mtl i) subs:subs
    )
    subs
)
引数のmtlにはマテリアルオブジェクトを指定してください。 例えば・・・
getAllSubMtlAndMaps $.material -- 選択オブジェクトのマテリアル

getAllSubMtlAndMaps meditMaterials[1] -- マテリアルスロット1のマテリアル
といった感じです。

オプション引数のsubsは内部使用専用なので、基本的には何も指定しないでください。

内部的な処理を簡単に説明すると、マテリアルのサブマテリアルとサブテクスチャを全て検索し、それらの要素に対してgetAllSubMtlAndMapsを再帰的に実行しています。
ただし、この時サブ要素がループ接続されている可能性を考慮し(実際にそういう状況になるのかは知りません…)、appendIfUniqueで要素がまだ追加されていない時だけ再帰しています。
よって、subsに最初から要素を追加しておいた場合、そのマテリアル以下は検索されない事になります。

ただし、取得されるサブ要素は、マテリアルとテクスチャマップが混ざってしまっているので、個別に必要な場合は自力で分類する必要があります。
local subs = getAllSubMtlAndMaps $.material
local mtls = #()
local maps = #()
for s in subs do
(
    if (superclassof s) == material then
        append mtls s
    else
        append maps s
)
こんな感じです。

余談ですが、自分が初めて再帰関数を使ったとき、再帰内でループする事に気付かず、処理がフリーズする原因を見つけられずに3日くらい悩んだ思い出があります。
再帰処理は非常に強力ではありますが、度々バグの原因になったりして扱いの難しさを時々実感します。

Related Articles