チュートリアル - TreeView ActiveX コントロールを使用したシーン ブラウザの開発 - 第 2 部

このチュートリアルの第 2 部では、第 1 部で定義したTreeView ActiveX コントロールをカスタマイズし、オブジェクト クラスによってアイコンを含め、ワイヤフレーム カラーを表示し、シーン ノードの非表示状態を制御する方法を示します。

注:

ActiveX コントロールは、DotNet フレームワークとそのコントロールを考慮して、最新バージョンの Microsoft Windows オペレーティング システムで非推奨となりました。

MAXScript では引き続き ActiveX コントロールがサポートされますが、これらは MAXScript にアクセス可能なシステムにインストールおよび登録する必要があります。

ActiveX コントロールの代わりに、MAXScript では、3ds Max 9 以降において DotNet コントロールがサポートされます。

ActiveX TreeView コントロールの DotNet TreeView コントロールへの変換」を参照してください

関連トピック:

MAXScript ロールアウト内の ActiveX コントロール

TreeView ActiveX コントロール

全体の流れ:

既存の macroScript を拡張します。

3ds Max 付属のアイコン ファイルから 1 つのアイコンを取り込む関数を定義します。

initTreeView 関数を拡張し、ほとんどのオブジェクト タイプについてアイコンを生成します。

各シーン ノードについて作成されたノードのそれぞれにアイコンを割り当てます。

各ノードのカラーをシーン ノードのワイヤフレーム カラーに設定します。

各ノードのチェック状態を、シーン ノードの非表示状態の反対に設定します。

TreeView 内のチェックボックスを有効にします。

表示するアイコンの保持と管理を行うために ActiveX コントロールをもう 1 つ定義します。

対応するチェックボックスにチェックマークが付いている場合/付いていない場合にオブジェクトを表示/非表示にするイベント ハンドラを追加します。

スクリプト:

    macroScript SceneTreeView category: "HowTo"
    (
    rollout treeview_rollout "TreeView Scene Browser"
    (
    fn getIconFromBitmap thePath number =
    (
     tempBmp = openBitmap thePath
     iconBmp = bitmap 16 15
     for v = 0 to 14 do
     setPixels iconBmp [0,v] (getPixels tempBmp [(number-1)*16, v] 16)
     iconBmp.filename = getDir #image +"/_temp.bmp"
     save iconBmp
     close iconBmp
     close tempBmp
     getDir #image +"/_temp.bmp"
    )

    fn initTreeView tv ilTv =
    (
     tv.Indentation = 28*15
     tv.LineStyle = #tvwRootLines
     tv.checkboxes = true
     tv.sorted = true
     ilTv.imagewidth = 16
     ilTv.imageheight = 15
     iconDir = (getDir #ui) +"\\icons"
     ilTv.listImages.add 1 #root (loadPicture (getIconFromBitmap (iconDir +"\\Standard_16i.bmp") 2))
     ilTv.listImages.add 2 #geomnode (loadPicture (getIconFromBitmap (iconDir +"\\Standard_16i.bmp") 1))
     ilTv.listImages.add 3 #light (loadPicture (getIconFromBitmap (iconDir +"\\Lights_16i.bmp") 3))
     ilTv.listImages.add 4 #camera (loadPicture (getIconFromBitmap (iconDir +"\\Cameras_16i.bmp") 2))
     ilTv.listImages.add 5 #helper (loadPicture (getIconFromBitmap (iconDir +"\\Helpers_16i.bmp") 1))
     tv.imageList = ilTv
    )

    fn addChildren tv theNode theChildren =
    (
     for c in theChildren do
     (
      theIcon =case superclassof c of
      (
       GeometryClass: 2
       Light: 3
       Camera: 4
       Helper: 5
       Default:2
      )
      newNode = tv.Nodes.add theNode.index 4 "" c.name theIcon
      theNode.sorted = true
      newNode.checked = not c.isHidden
      newNode.forecolor = color c.wirecolor.b c.wirecolor.g c.wirecolor.r
      addChildren tv newNode c.children
     )
    )

    fn fillInTreeView tv =
    (
     theRoot = tv.Nodes.add()
     theRoot.image = 1
     theRoot.text ="WORLD ROOT"
     rootNodes = for o in objects where o.parent == undefined collect o
     addChildren tv theRoot rootNodes
    )

    activeXControl tv "MSComctlLib.TreeCtrl"width:290 height:490 align:#center
    activeXControl ilTv "MSComctlLib.ImageListCtrl" height:0 width:0
    on tv NodeCheck theNode do
    (
     theSceneNode = (getNodeByName theNode.text)
     if theSceneNode != undefined do
      theSceneNode.isHidden = not theNode.checked
    )

    on tv nodeClick theNode do try(select (getNodeByName theNode.text))catch()
    on treeview_rollout open do
    (
     initTreeView tv ilTv
     fillInTreeView tv
    )
    )
    try(destroyDialog treeview_rollout)catch()
    createDialog treeview_rollout 300 500
    )

結果:

ステップごとの解説

macroScript SceneTreeView category:"HowTo"
(
rollout treeview_rollout "TreeView Scene Browser"
( fn getIconFromBitmap thePath number = (

この新しい関数では、3ds Max に付属のアイコンを取り込んで再利用できます。この関数に渡す引数は、取得するアイコン ビットマップ ファイルのパスおよびアイコン番号です。ここでは、16 x 15 の小さいアイコンだけを使用することを想定しています。関数に変更を加え、24 x 24 のアイコンに対応するようにもできますが、このサイズのアイコンでは TreeView の見た目がよくありません。

tempBmp = openBitmap thePath

まずアイコン ファイルへのパスの入ったビットマップを開きます。アイコン ファイルには任意の数のアイコンを含めることができます。小さいサイズの 16 x 15 ピクセルのアイコン ファイルは、高さ 15 ピクセルで、幅はアイコンの数に 16 をかけた値です。

iconBmp = bitmap 16 15

次に、16 x 15 ピクセルという単一アイコンのサイズの空のビットマップを作成します。

for v = 0 to 14 do

ループは、0 から 14 までです(ビットマップでのピクセル アクセスは基数 0 なので、0 から 15 マイナス 1 になります)。

setPixels iconBmp [0,v] (getPixels tempBmp [(number-1)*16, v] 16)

アイコンの各行について、getPixels を使用し、アイコンの先頭から 16 ピクセルを取り込みます。(Number-1)*16 は最初のアイコンについては 0 を返し、以後 2 番目のアイコンについては 16、3 番目のアイコンは 32、というように続きます。次に、16 ピクセルの行全体をあらかじめ定義した iconBmp ビットマップに割り当てます。左端のピクセル (0) から開始し、同じ行数(v)を処理します。その結果、指定した番号のアイコンが新しい空のビットマップにコピーされます。

iconBmp.filename =GetDir #image +"/_temp.bmp"

ここで、新しいアイコンのビットマップの名前を 3ds Max の \¥Images サブディレクトリ内の一時ビットマップ ファイルに設定します。この設定が必要となるのは、ビットマップを OLE 対応のアイコンに変換する loadPicture メソッドで、保存された .BMP ファイルまたは .ICO ファイルが必要になり、ディスクに保存できるのが .filename プロパティを設定したビットマップだけであるためです。

save iconBmp

ファイル名を定義したので、この単一アイコンをディスクに保存できます。

close iconBmp close tempBmp

次に、オリジナルの複数アイコンのファイルと、1 つのアイコンだけが入った一時ファイルの両方を閉じます。

GetDir #image +"/_temp.bmp"

この関数の結果、新しい単一アイコンのファイル名が返されます。

 )

fn initTreeView tv ilTv = (

これは TreeView を初期化する既存の関数ですが、ここでは 2 つ目の引数を渡します。 アイコン管理のための ImageListCtrl ActiveX コントロールです。

tv.Indentation = 28*15
tv.LineStyle = #tvwRootLines

tv.checkboxes = true

今回は、すべてのノードの前にチェックボックスを表示します。チェックボックスは、シーン ノードの非表示状態を表示し制御するために使用されます。

tv.sorted = true

また、ツリーをアルファベット順にソートします。

ilTv.imagewidth = 16 ilTv.imageheight = 15

アイコン イメージのサイズを、アイコン ファイルから取り込むアイコンと同じサイズに定義します。

iconDir = (getDir #ui) + "\\icons"

これは、付属のアイコン ファイルの既定の場所です。

ilTv.listImages.add 1 #root (loadPicture (getIconFromBitmap (iconDir + "\\Standard_16i.bmp") 2))

アイコンの管理に使用する ImageListCtrl の .listImages プロパティの .add() メソッドを呼び出します。追加する最初のイメージは、ルート ノードだけに使用します。先に定義した getIconFromBitmap 関数を呼び出し、Standard ジオメトリ プリミティブと番号 2 のアイコン (球) を含むアイコン ファイルを渡します。前に定義した getIconFromBitmap 関数を呼び出し、標準ジオメトリ プリミティブを含むアイコン ファイルとアイコン番号 2 (球)を渡します。結果のパスは loadPicture メソッドに渡されます。このメソッドは一時アイコン ファイルである .bmp ファイルを開き、結果の OLE 対応アイコン イメージを ImageListCtrl に割り当てます。

ilTv.listImages.add 2 #geomnode (loadPicture (getIconFromBitmap (iconDir + "\\Standard_16i.bmp") 1))

全体を繰り返しますが、今回はジオメトリ ノードに使用する 2 つ目のイメージを作成します。番号 1 のアイコン (ボックス) を取り込みます。

ilTv.listImages.add 3 #light (loadPicture (getIconFromBitmap (iconDir + "\\Lights_16i.bmp") 3))

ライトに使用する 3 つ目のイメージも同様に行います。ただし、Lights アイコン ファイルの 3 つ目のアイコン (オムニ) を指定します。

ilTv.listImages.add 4 #camera (loadPicture (getIconFromBitmap (iconDir + "\\Cameras_16i.bmp") 2))

カメラは番号 4 のイメージになります。 Cameras アイコン ライブラリの 2 つ目のアイコンを取り込みます。

ilTv.listImages.add 5 #helper (loadPicture (getIconFromBitmap (iconDir + "\\Helpers_16i.bmp") 1))

最後に、Helpers アイコン ファイルの最初のアイコン (ダミー) を取り込む 5 つ目のイメージを定義します。

まだ定義していないクラスがありますが (シェイプ、スペース ワープなど)、上記のコードを同様に拡張することで、独自のアイコンを実装することができます。

tv.imageList = ilTv

最後に、TreeView の .imageList プロパティに ImageListCtrl コントロールを割り当てます。こうすると、上のイメージが TreeView でアイコンとして使用できるようになります。

)

fn addChildren tv theNode theChildren =
(
for c in theChildren do
( theIcon = case superclassof c of

コントロール内にアイコンが表示できるようになったので、関数によって処理されている 3ds Max のシーン ノードのスーパークラスを確認し、そのタイプに基づいて適切なアイコンを定義することができます。theIcon 変数には、使用するアイコンに対応する整数が入ります。GeometryClass:

 ( GeometryClass: 2

ジオメトリ オブジェクトはアイコンにイメージ 2 を使用します。

Light: 3

ライトはイメージ 3 を使用します。

Camera: 4

カメラはイメージ 4 を使用します。

Helper: 5

ヘルパーはイメージ 6 を使用します。

Default:2

ほかは、それぞれのアイコンができるまでジオメトリのアイコンを使用します。

) newNode = tv.Nodes.add theNode.index 4 "" c.name theIcon

アイコンのインデックスを第 1 部で行ったように 0 に設定する代わりに、アイコンを上記で定義した値に設定します。

theNode.sorted = true

階層のすべてのレベルをアルファベット順にソートします。

newNode.checked = not c.isHidden

ノードのチェック状態は .isHidden プロパティの逆(ノードが非表示になっているときに true)になります。ノードが非表示ではない場合にボックスにチェックマークを付けたいので、このように明示します。

newNode.forecolor = color c.wirecolor.b c.wirecolor.g c.wirecolor.r

TreeView の各ノードのカラーを完全に制御できるようになったので、ワイヤーフレーム表示のカラーを取り込んで .forecolor プロパティに割り当てます。こうすると、シーン オブジェクトのカラーをカラフルに参照できます。

オブジェクトに標準マテリアルが割り当てられている場合には、マテリアルの拡散反射光カラーを取り込んだり、カラーで別のプロパティを表したりするように拡張することもできます。また、TreeView ノードの .bold プロパティを true に設定し、3ds Max オブジェクトの他のプロパティを表すようにすることもできます。

addChildren tv newNode c.children
)
)

fn fillInTreeView tv =
(
theRoot = tv.Nodes.add()

theRoot.image = 1

ImageListCtrl の最初のイメージは、球のアイコンを示すように定義済みです。これは、ルート ノードを表すために使用します。

theRoot.text = "WORLD ROOT"
rootNodes = for o in objects where o.parent == undefined collect o
addChildren tv theRoot rootNodes
)

activeXControl tv "MSComctlLib.TreeCtrl" width:290 height:490 align:#center activeXControl ilTv "MSComctlLib.ImageListCtrl "height:0 width:0

これは、TreeView でアイコンの管理に使用する ImageList ActiveX コントロールです。ロールアウトに表示する必要はないので、高さと幅を 0 に設定します。

on tv NodeCheck theNode do (

ユーザがノードのチェックボックスの状態を変更すると、

theSceneNode = (getNodeByName theNode.text)

テキスト ラベルを実際のオブジェクト名に変換することによって、それがどのシーン ノードを表すのかを確認します。

if theSceneNode != undefined do

確認に成功した場合、

theSceneNode.isHidden = not theNode.checked

シーン ノードの .isHidden プロパティを TreeView ノードのチェック状態の逆の値に設定します。ユーザがチェックボックスのチェックマークを外した場合、theNode.checked は false を返します。これは not によって true に変換され、非表示プロパティに割り当てられ、効果的にノードを非表示にします。

)

on tv nodeClick theNode do try(select (getNodeByName theNode.text))catch()
on treeview_rollout open do
(
initTreeView tv ilTv
fillInTreeView tv
)
)
try(destroyDialog treeview_rollout)catch()
createDialog treeview_rollout 300 500
)