Java3Dに触れてみる


技術評論社 JavaPress vol.7 に掲載された記事を公開します (無断転載禁止)。

はじめに / はじめよう / 簡単 / 複雑 / SimpleUniverse / 最適化 / おわり / 戻る / トップページ


はじめに

Java3D は Java で 3 次元グラフィクスや 3 次元音響を実現する パッケージです。 これまで、Java でこれらの機能を実現しようとした時には 自分でレンダリングエンジンを作ったり、 OpenGL などを呼び出すネイティブコードを書いたり、 利用したりしなければいけませんでした。 昨年末についに Java3D の正式版がリリースされ、 Java の正式な 3 次元パッケージが用意されました。 これでやっと面倒なコードを書く必要がなくなり、 3 次元グラフィックスや 3 次元音響が楽しめるようになりました。
 
本章では Java3D の 3 次元グラフィクスに関する 説明を書いていきたいと思います。 私自身 OpenGL や Direct3D などの 3 次元グラフィックスライブラリを 使ったことがありませんでしたが、簡単に Java3D が使えました。 そういった意味では Java3D を使うために 高度な知識は必要ないと思いますので、ご安心下さい。

Java3D をはじめよう

動作環境

Java3D の最新バージョンは 1.1.2 です (1999/6 現在)。 バージョン 1.0 は仕様のみで実装はされませんでした。
 
Java3D はその内部で OpenGL や DirectX を呼び出しています。 そのため、Java3D を使って 3 次元グラフィクスや 3 次元音響のプログラムを書く事で プラットフォーム非依存の 3 次元処理ができます。 もちろん 3 次元処理機能を持つハードウェアがある場合、 その機能を利用してくれるので高速処理が可能です。
 
今のところ、Windows 95 / 98 / NT4.0 と Solaris でのみ動作します。 Java3D にはネイティブコードが含まれているために その他の機種は対応していませんが、 Java3D 対応機種の範囲では Write Once, Run Anywhere は保たれています。 また、Java2 (JDK1.2) が必要です。

インストール

まず、JDK 1.2, Java3D をダウンロード・インストールして下さい (http://java.sun.com/products/java-media/3D/index.html)。
 
Windows 95 の OSR2.0 より前のバージョン で Java3D OpenGL 版を使う場合は、 OpenGL 1.1 (http://www.opengl.org/Developers/Implementations/Win.html) もインストールして下さい。
 
Windows NT4.0 でインストールする際には注意が必要です。 Windows NT では、環境変数 PATH の先頭に C:\WINNT\SYSTEM32 が含まれていますが、 JDK 1.2 のインストーラーはここに java.exe, javaw.exe をコピーします。 しかし、C:\WINNT\SYSTEM32 配下に lib/ext ディレクトリーが無いので、 Java3D のクラスがロードできず、 Java3D を使ったアプリケーションを実行しようとすると 例外が発生してしまいます。 対処方法として環境変数 PATH の先頭に、C:\jdk1.2.1\bin を記述するか C:\WINNT\SYSTEM32 の java.exe、javaw.exeを削除するかして下さい。
 
無事、ダウンロードできたら HelloUniverse デモを実行してみましょう。 jdk のフォルダの下にある demo/java3d/HelloUniverse フォルダに移動し、 「java HelloUniverse」で実行してみて下さい。 立方体がくるくる回っていたらインストール成功です。 同じように「appletviewer HelloUniverse.html」として アプレットとしても実行できます。

Linux 等で Java3D を動かすには

正式な対応機種は上記の通りですが、 実は IRIX, Linux 等で Java3D を動かすこともできます。 一部未実装の機能はありますが、Java3D を実装した方々がいるのです。
 
JFree-D media.j3d package (http://spia.freenix.fr/~jct/) と vecmath package (http://www.esm.co.jp/java/vecmath/index-j.html) です。 この二つを組み合わせることで IRIX や Linux でも Java3D が使えますし、 JDK 1.1 上で動きます。 JoGL (OpenGL binding for Java) (http://www.pajato.com/jogl) が動く環境であれば他の環境でも動くようです。
 
JFree-D のホームページの説明にそって JoGL, JFree-D, vecmath package の順にダウンロード・インストールすれば Java3D が使えるようになります。 立体の文字も扱いたい時は TrueType Font (http://providenet.softseek.com/Graphics_and_Drawing/Fonts/) も必要です。
 
また、Linux x86 では Java3D pre-v1 (http://www.blackdown.org/java-linux/jdk1.2-status/java-3d-status.html , http://nitric.com/pub/java-linux/java3d/1.1.1/i386/) というものもあります。 こちらはほとんどの機能が実装されていますし、 完成度も高く、インストールも楽ですので Linux x86 ユーザーはこちらを使うことをお勧めします。

まずは簡単なところから

早速、プログラミング

細かい話をする前にまず簡単なプログラムをしてみましょう。 説明は後からしていきます (リスト1, 図1)。
 
(図1 Hello3D の実行結果)
 
このプログラムは立方体を斜め上から見下ろした画像を表示するものです。
 
3〜6 行目を見ると何種類かのパッケージが使われているのがわかります。 Java3D 本体のクラスは javax.media.j3d パッケージに、 幾何学クラス (行列やベクトル) は javax.vecmath パッケージに、 分類されます。 他にもプログラムを補助する便利なクラスの入った com.sun.j3d.utils パッケージもあります。 このユーティリティーパッケージは必ずしも使う必要はありませんが、 役に立つものも多いため良く使われます。

レンダリングモード

それでは、解説に入りましょう。
 
Java3D のグラフィック処理では 3 つのレンダリングモードがあります。 Immediate Mode, Retained Mode, Compiled-Retained Mode の 3 つです。 同じ処理をするにもこの 3 つの方法があり、それぞれ長所短所があります。
 
Immediate Mode は 1 つの物体の描画などの簡単な機能のみが用意されていて、 プログラマーはそれを使って複雑な処理を書いていく方法です。 描画タイミングの制御など融通が利きますが、 プログラマーに負担のかかる方法でもあります。 自分でレンダリングエンジンを書きたい人はこのモードを使いましょう。
 
Retained Mode は SceneGraph という木構造を利用する方法です。 プログラマーはその SceneGraph を制御する事で 3 次元処理を行います。 Immediate Mode よりも簡単でわかりやすく、 きれいなプログラムが書けます。 特別な理由がないのであればこのモードを使った方がよいでしょう。
 
Compiled-Retained Mode は SceneGraph を実行時にコンパイルすることで高パフォーマンスを獲得する Retained Mode です。 以後、特に断らない限り Retained Mode と書いたら Compiled-Retained Mode も含まれると考えてください。
 
今回は Retained Mode を使います。
 
先ほどのサンプル Hello3D でも Retained Mode を使っています。 Hello3D のように Retained Mode では SceneGraph を作っていけば (createSceneGraph() メソッド) 表示は自動的にされます。 SceneGraph が変化すればそれに伴って表示される絵も変化します。

SceneGraph

SceneGraph とは Java3D の機能の中心となるもので、 Java3D を理解する上で必要不可欠な概念です。 表示される物体などの情報を木構造で表現し、 各ノードはクラスで表現されます。
 
Java3D では子を持たないノード (葉) を Leaf クラスのサブクラスとして、 子を持つノードを Group クラスのサブクラスとして定義しています。
 
実際には表示に関する全ての情報を SceneGraph として構築して初めて表示ができますが、 簡単に表示できるようにある程度の情報を SceneGraph として構築してくれる SimpleUniverse クラスがあります。 SimpleUniverse クラスは良く使われるユーティリティークラスの一つで そのコンストラクタ中で SceneGraph に必要なノードを作ってくれますので、 今回はそれを使ってみます。 SimpleUniverse オブジェクトがそのまま SceneGraph の根となります。
 
SimpleUniverse ノードの子には BranchGroup ノードをぶら下げる必要があります。 BranchGroup ノードは Group の一つで、 複数の子を持つことを目的としたノードです。
 
何か物体を表示させたい時はその物体を表現する Shape3D ノードを生成し、 BranchGroup ノードの下にぶら下げます。
 
しかし、そのままでは物体は原点に表示されてしまいます。 別の位置に物体を表示したい場合は BranchGroup ノードの下に 物体の位置を表現する TransformGroup ノードをぶら下げて、 さらにその下に Shape3D ノードをぶら下げます。
 
一般的には図 2 のような SceneGraph が出来上がります。 各ノードの意味を表 1 に示します。
 
(図2 SceneGraph)
 
ノード (=クラス)種類 意味
SimpleUniverse 3次元空間全体
BranchGroup Group 複数の子ノードをまとめる
TransformGroup Group 物体の位置
Shape3D Leaf 物体
(表1 ノードの意味)

サンプル Hello3D の解説

さて、ここでサンプルプログラム Hello3D の解説をしていきましょう。
 
まず、init() メソッドから。 Java3D の出力画像は AWT である Canvas3D オブジェクトに対して表示されます。 Canvas3D クラスは通常の AWT と同じように扱います (15, 22, 23 行目)。
 
Canvas3D オブジェクトを引数にして SimpleUniverse ノードを構築し、 SceneGraph の根っこを作ります (16 行目)。 次に BranchGroup ノードを作り、 addBranchGraph(BranchGroup bg) メソッドで SimpleUniverse ノードの下にぶら下げます (20 行目)。
 
BranchGroup ノード以下は createSceneGraph() メソッドで作っています。 SimpleUniverse 以外のノードに対して 別のノードをぶら下げるには addChild(Node child) メソッドを使用します。
 
この例で表示する物体には ColorCube ノードを使っています。 ColorCube クラスはユーティリティーパッケージに含まれている Shape3D クラスのサブクラスの一つで、 テストに使う面ごとに色の違う立方体です (46 行目)。
 
先ほど TransformGroup ノードは物体の位置を表現すると書きましたが、 TransformGroup ノードには移動を示す Transform3D オブジェクトを設定することで 物体の位置を設定します (34〜44 行目)。

Transform3D クラス

Transform3D クラスについてさらに詳しく説明していきます。
 
まず、その準備として座標系について簡単に話をします。 Java3D で使われている座標系は 画面に向かって右方向が x 軸、 上方向が y 軸、 画面から手前方向が z 軸のそれぞれ正方向です (右手系)。
 
Java3D で使われる座標系の中で今考える必要があるのは ローカル座標系と (仮想) ワールド座標系です。 ワールド座標系とは文字通り私たちのいる世界での座標系です。 ローカル座標系とは各物体ごとにある座標系で、 一般的には物体の中心が原点にあたるような座標系です。
 
そして、Transform3D クラスの正体ですが、 このクラスは座標変換を行う変換行列です。 TransformGroup ノードはその下に ぶら下がっているノードの座標系に Transform3D オブジェクトを掛け合わせて 座標変換を行っています。
 
つまり、物体の位置・形・向きは一般的にローカル座標系で定義されており、 TransformGroup ノードの Transform3D オブジェクトによって ワールド座標系上での位置・形・向きに変換するわけです。
 
Transform3D クラスで良く使われるメソッドを表 2 に示しておきます。
 
メソッド 変換
setTranslation(Vector3f trans) 方向 trans へ平行移動
rotX(double angle) X 軸を中心に angle ラジアン回転
rotY(double angle) Y 軸を中心に angle ラジアン回転
rotZ(double angle) Z 軸を中心に angle ラジアン回転
mul(Transform3D t1, Transform3D t2) 2 つの変換を合成 (t2→t1 の順で変換される)
(表2 良く使われる Transform3D のメソッド)

ちょっと複雑に

ちょっと長めのプログラム

さて、ここからは物体を表示したい、光源が欲しいなどの 目的を達成するためには具体的にどうすればいいかを説明していきます。
 
ちょっと長めですが、ここからの解説の例として リスト 2, 3 を使用します。 リスト 2, 3 の出力画像は図 3 のようになります。
 
(図3 Tunnel の実行結果)
 

vecmath パッケージ

まず、これからよく使うことになる vecmath パッケージについて説明しておきましょう。
 
javax.vecmath パッケージは行列やベクトルなど 幾何学情報を表現するために使われるクラスで構成されています。
 
どのクラスも [種類名][次元数][型名] という形で名前が決まっています。 例えば、double 型の 4 次元ベクトルなら Vector4d といった具合です。
 
種類名を表3に示します。 Matrix4f クラスも Transform3D クラスも4行4列の行列ですが、 Matrix4f クラスは一般的な行列、 Transform3D クラスは変換行列と使い分けられています。
 
また、Java3D では色を java.awt.Color クラスではなく vecmath パッケージのクラスで表現します。 これは光源などで様々な演算を行うためです。
 
種類名 意味
Point
Vector ベクトル
Matrix 正方行列
Color
TexCoord テクスチャ座標
AxisAngle軸ベクトルとその軸周りの回転角
Quat クォータニオン
Tuple Point, Vector などの親クラス
(表3 vecmath の種類名)
 

物体の定義

物体を定義するには Shape3D オブジェクトを作ります。 Shape3D オブジェクトは Geometry オブジェクトと Appearance オブジェクトから構成されており、 Geometry オブジェクトは物体の形状を Appearance オブジェクトは物体の見かけの情報を保持しています。
 
Shape3D オブジェクトを作るには少なくとも Geometry オブジェクトを指定する必要があります。 Geometry オブジェクトは Geometry クラスのサブクラスで実体化します。 3 角形の面で構成される形状を作りたい時は TriangleArray クラスを、 4 角形の面で構成される形状を作りたい時は QuadArray クラスを使います。
 
例えば、水晶の形をした物体を定義するにはリスト 3 のようにします。 この場合は Shape3D クラスのサブクラス Crystal を定義し、 その中で Geometry を設定しています。
 
3 角形の面で構成される形状を作る場合、 コンストラクタ TriangleArray(int vertexCount, int vertexFormat) の引数の vertexCount には頂点の数を、 vertexFormat には TriangleArray クラスに定義されている定数を指定します。
 
頂点の数は 3 の倍数である必要があります (3 角形の面で構成されるため)。 普通は vertexCount = 3 × 面の数 とします。 3 の倍数以外を指定すると IllegalArgumentException が発生します。
 
コンストラクタの vertexFormat に指定する定数は COORDINATES (頂点座標), NORMALS (法線ベクトル), COLOR_3 (色), COLOR_4 (透明色を含んだ色), TEXTURE_COORDINATE_2 (2 次元のテキスチャー座標), TEXTURE_COORDINATE_3 (3 次元のテキスチャー座標) のうち定義したい項目に対応するものを OR 演算でつなげたものを指定します。 この例では頂点座標と法線ベクトルを設定するため、 TriangleArray.COORDINATES | TriangleArray.NORMALS としています。
 
そして生成された TriangleArray オブジェクトに対して setCoordinates(int index, Point3f[] coordinates) メソッドや setNormals(int index, Vector3f[] normals) メソッドで 頂点座標や法線ベクトルを定義することで Geometry オブジェクトの完成です。 各面の頂点座標は物体の内側から見て右回りになるように定義してください。

便利な物体 Primitive

Primitive とはユーティリティークラスの一つで 基本的な形状の物体を表現します (com.sun.j3d.utils.geometry.Primitive クラスのサブクラスです)。 Shape3D ノードではありませんが、 同じように SceneGraph に追加することで表示ができます。
 
Box (直方体)、Cone (円錐)、 Cylinder (円柱)、Sphere (球) の 4 つがあります (いずれも com.sun.j3d.utils.geometry パッケージ)。 これらの形状を表示したい時には非常に便利なクラスです。
 
どのクラスもコンストラクタに int primflags という引数が用意されています。 ここに Primitive クラスの定数を OR でつなげる事で 法線ベクトルやテクスチャ座標も自動的に定義してくれます。 法線ベクトルを定義して欲しい時は GENERATE_NORMALS、 テクスチャ座標を定義して欲しい時は GENERATE_TEXTURE_COORDS を指定します。 GENERATE_NORMALS とともに GENERATE_NORMALS_INWARD を指定すると 法線ベクトルを物体の内側方向に定義してくれます。
 
例えば、リスト 2 の 84〜89 行目では物体の内側方向への法線ベクトル、 テクスチャ座標を自動定義しています。

物体の見かけの変更

物体の見かけを変えるにはその物体の Shape3D (Primitive) ノードに 参照されている Appearance オブジェクトを変更します。 Appearance オブジェクトはさらに 照光のための情報を保持する Material オブジェクトや Texture オブジェクト、 *Attributes オブジェクト (* にはいろいろな名前が入ります) など各種のオブジェクトへの参照で構成されています。
 
物体の見かけを変えるためにはそれらのオブジェクトの情報を変更します。 それでは、テクスチャ、半透明について具体的に見てみましょう。

テクスチャの貼り方

物体にテクスチャを貼るには Texture クラスを使います。 Appearance オブジェクトに対して setTexture(Texture texture) メソッドで Texture オブジェクトを指定します。
 
Texture オブジェクトを生成するのに便利なユーティリティークラス、 com.sun.j3d.utils.image.TextureLoader クラスが用意されています。 このクラスは BufferedImage オブジェクトや ファイル名で指定した画像ファイルを元に Texture オブジェクトを作ってくれます。 リスト 2 の 77, 78 行目がその例です。
 
また、そのテクスチャを貼り付ける Shape3D (Primitive) ノード にはテクスチャ座標を設定する必要があります。

半透明の物体の作り方

物体を半透明にするには TransparencyAttributes クラスを使います。 Appearance オブジェクトに対して setTransparencyAttributes(TransparencyAttributes transparencyAttributes) メソッドで TransparencyAttributes オブジェクトを指定します。
 
TransparencyAttributes クラスは透明度を指定するクラスで、 コンストラクタの引数に色の透け方と透明度を指定します。 透明度は 0.0〜1.0 の範囲で 1.0 が完全な透明です。 リスト 2 の 148〜151 行目がその例になっています。

光源の追加

光源は Light クラスのサブクラスによって表現されています。 そのオブジェクトを SceneGraph にぶら下げます。 このノードは SceneGraph のどこにぶら下げても効果は同じです。
 
さらに、光源の有効範囲を指定する事、 物体の表面に光源の影響が及ぶようにする事、 物体の法線ベクトルを定義する事が必要です。
 
光源の有効範囲とは光の及ぶ範囲ではなく、 視点と光源の有効範囲が重なった時のみ光源を処理するという意味で、 パフォーマンス向上のための機能です。
 
有効範囲は範囲を示す Bounds オブジェクトを作り、 Light オブジェクトに対して setInfluencingBounds(Bounds region) メソッドで設定します。 良く使われる Bounds クラスとして 球で範囲を指定する BoundingSphere クラスがあります。 範囲を特に気にしない場合は半径を無限大に設定した BoundingSphere オブジェクトが使えます。
 
リスト 2 の createLight() メソッドがそれらの処理を行っています。 この例では点光源を使います。 点光源は電球のような比較的小さな光源がある程度離れた物体を照射する時に 光源の大きさを無視しても問題ないような場合に使われます。
 
物体の表面に光源の影響が及ぶようにするには、 まず Material オブジェクトを作り、それに setLightingEnable(boolean state) メソッドで光源の影響が及ぶようにし、 物体の Appearence オブジェクトにその Material オブジェクトを登録します。
 
リスト 2 では 3 つの物体がありますが、 80〜82, 108〜112, 141〜146 行目でそれぞれこの処理を行っています。
 
物体の法線ベクトルを定義する方法は先ほど述べた通りです。

サンプル Tunnel の解説

サンプルプログラム Tunnel についても簡単に解説をしましょう。
 
このプログラムは円柱の内側にテクスチャを貼り、 視点がその中に入るようにすることでトンネルの中に居るように見せています (createTunnel() メソッド)。
 
そして、トンネルの中に球体 (createBall() メソッド) と 半透明の水晶型の物体 (createCrystal() メソッド) を配置し、 点光源がトンネルと球と水晶を照らしています。
 
createCrystal() メソッドでは TransformGroup ノードを 2 段階にぶら下げています。 このようにした場合は SceneGraph の根からより遠いノードから 座標変換が適用されていきます。
 
リスト 2 のコードで図 4 の SceneGraph ができます。
 
(図4 サンプル Tunnel の SceneGraph)

SimpleUniverse の正体

今まで、SceneGraph の根を SimpleUniverse として話をしてきましたが、 実際には根のノードはもっと細かく分かれています。
 
本当の SceneGraph の根は VirtualUniverse ノードで、 その子に Locale ノード (javax.media.j3d.Locale です。 java.util.Locale とは違いますので注意してください) があります。 VirtualUniverse ノードは 3 次元空間全体を表現し、 Locale ノードはその広大な空間の一部を表現しています。 SimpleUniverse は Locale ノードが一つの場合の VirtualUniverse ノードだったのです。
 
SimpleUniverse はそのコンストラクタ中で Locale ノードの下に ViewingPlatform ノードを構築しています (図5)。 そのさらに下にも様々なノードが追加されていますが、 これらは視点に関する情報を表現しています。 そのうち ViewPlatform ノード (ViewingPlatform と名前が似ているので注意してください) が視点を表現しています。
 
(図5 本当の SimpleUniverse)
 
ViewPlatform ノードの上にある MultiTransformGroup (TransformGroup クラスのサブクラス) ノードの 持っている Transform3D オブジェクトを 変更することで視点位置が変更できます。 視点位置・方向の設定は物体と全く同じ方法です。 物体の場合は TransformGroup ノードが ローカル座標系からワールド座標系への変換を表していたのに対して 視点の場合は TransformGroup ノードが カメラ座標系からワールド座標系への変換を表しています。
 
視点位置を表す TransformGroup ノードを取得するには SimpleUniverse オブジェクトから getViewingPlatform() メソッドで ViewingPlatform ノードを取得し、 さらに getViewPlatformTransform() メソッドで TransformGroup ノードを取得します。
 
例えば次のようにします。
	Transform3D translation=new Transform3D();
	translation.setTranslation(new Vector3d(0.0,0.0,3.0));
	// 視点を (0.0,0.0,3.0) に設定
	
	TransformGroup transform=
	universe.getViewingPlatform().getViewPlatformTransform();
	transform.setTransform(translation);

最適化の話

Java3D には最適化のための機能が用意されており、 それらを利用することで マシンのパフォーマンスを最大限に引き出すことができます。 ここからは最適化のための機能について紹介していきます。

Capability bit

これまでの説明で一通りのことはできるようになったと思いますが、 SceneGraph を動的に構築しようとすると浮上してくる問題として、 Capability bit があります。
 
SceneGraph の根からつながる個所にノードをぶら下げた後に、 そのノードの属性を変更したり、さらにその下にノードを追加しようとすると CapabilityNotSetException が発生します。 これは Capability bit を設定していないために起こります。
 
Capability bit とは SceneGraph のノードの属性の動的な変更を 許可するためのフラグです。 通常どの属性の変更も許されていないので、 動的に属性を変更したい時は Capability bit をセットする必要があります。
 
SceneGraph を構成するクラスには Capability bit をセットするメソッド setCapability(int bit) が用意されています。 Capability bit をセットしたいオブジェクトに対して そのクラス (または親クラス) に用意されている ALLOW_ で始まる定数を必要な数だけ OR 演算でつなげて引数とし、 setCapability(int bit) メソッドを呼び出して Capability bit をセットします。
 
例えば、BranchGroup ノードの下に新しいノードをぶら下げたい場合は BranchGroup.ALLOW_CHILDREN_EXTEND を、 テクスチャーを読み書きしたい時は Appearance.ALLOW_TEXTURE_READ | Appearance.ALLOW_TEXTURE_WRITE を setCapability(int bit) メソッドの引数に指定します。 SceneGraph 構築後に読みたい属性についても Capability bit をセットする必要がある点に注意してください。
 
また、一度 SceneGraph にぶら下げられたノードの下に ぶら下げられるのは BranchGroup ノードのみです。 親ノードには Capability bit の ALLOW_CHILDREN_EXTEND をセットします。 取り除けるのは Capability bit の ALLOW_DETACH をセットした BranchGroup ノードのみです。 BranchGroup 以外のノードを SceneGraph に動的に ぶら下げたり取り除いたりしたい場合は、 BranchGroup ノードの下にそのノードをぶら下げて、 SceneGraph に対して BranchGroup ノードをぶら下げたり取り除いたりします。

コンパイルで高速化

Capability bit の話が出てきたついでに Compiled-Retained Mode についても説明しておきましょう。
 
Compiled-Retained Mode は高パフォーマンスを獲得するために SceneGraph を実行時にコンパイルします。 Retained Mode との処理の違いは BranchGroup ノードに対して compile() メソッドでコンパイルをするだけです。 このとき、BranchGroup ノードの全ての子について Capability bit が設定されていない属性を 内部で高速に処理できる形式に変換しています。
 
そのため後から属性に変更を加える予定のあるオブジェクトには、 前もって Capability bit を設定しておく必要があります。

同じ物体を大量に表示

全く同じ物体を大量に表示させたい時には 中身が同じ Shape3D ノードを表示させたいだけ作らなければなりません。 ところが全く同じオブジェクトを大量に作れば メモリーを余分に消費してしまいますし、 色を一斉に変更したい時などは 全ての Shape3D ノードの色を変更しなければならないなど非効率です。
 
そこで SharedGroup クラスと Link クラスを使います。 これらのクラスは SceneGraph でリンクを実現するためのものです。
 
まず、Shape3D ノードなどの SceneGraph に大量にぶら下げたいノードを 1 つだけ作り、 SharedGroup ノードにぶら下げます。 そして、大量にぶら下げたいノードの代わりに Link ノードを SceneGraph にぶら下げます。 Link クラスのコンストラクタには先ほど作った SharedGroup オブジェクトを指定します。
 
例えば、リスト 1 の 46 行目を次のコードと入れ替えてみて下さい。 実行結果は同じになりますが、 新たに大きさ 0.4 の ColorCube が必要となった時には Link ノードを欲しい場所にぶら下げれば良いことになります。
	SharedGroup shared=new SharedGroup();
	shared.addChild(new ColorCube(0.4));
	
	root.addChild(new Link(shared));
SharedGroup ノードは SceneGraph にぶら下げずに使いますので その点は注意してください。 また、SharedGroup ノードも compile できます。

表示・非表示の切り替え

いざ Java3D を使ってプログラムをしていくと 頻繁に表示・非表示を繰り返す物体を作る必要がでてきたりします。 そのたびに SceneGraph からノードをはずしたり ぶら下げたりを繰り返すことになりますが、 SceneGraph へのノードの追加や削除自体に時間がかかる上に Java3D の最適化 (compile など) も受け難くなります。
 
そこでこの様な問題を解決するために Switch クラスが用意されています。 このクラスは Group の 1 つでその下にぶら下げられている ノード群の有効・無効を切り替えられるものです。
 
有効・無効にするノードの指定方法には 2 種類の方法が用意されています。
 
1 つめの方法は有効とするノードをインデックスで指定する方法です。 Switch ノードは通常の Group と同じように addChild(Node child) メソッドで別のノードをその下にぶら下げられます。 そして、 setWhichChild(int child) メソッドまたは コンストラクタ Switch(int whichChild) の引数に有効にするノードのインデックスを指定します。 インデックスは Switch ノードにぶら下げられた順に 0,1,2,... と付けられます。
 
例えば次のようにします。
	Switch selecter=new Switch();
	selecter.addChild(new Sphere());	// 球 (インデックス 0)
	selecter.addChild(new ColorCube());	// 色付き立方体 (インデックス 1)
	selecter.setWhichChild(0);		// 球を表示
この場合、インデックスで指定されていないノードは無効になります。 インデックスには全てのノードを有効にする定数 CHILD_ALL や全てのノードを無効にする定数 CHILD_NONE を指定する事もできます。
 
2 つめの方法はノードごとに有効・無効を指定する方法です。 上の方法でインデックスに定数 CHILD_MASK を指定し、 setChildMask(BitSet childMask) メソッドで有効・無効を一括設定します。
 
BitSet オブジェクトの 0 ビット目が 0 番目のノードを 1 ビット目が 1 番目のノードを、といった具合に 各ビットが各ノードの有効・無効を指定するフラグになっています。 例えば次のようにします。
	Switch selecter=new Switch(Switch.CHILD_MASK);
	selecter.addChild(new Sphere());        // 球 (インデックス 0)
	selecter.addChild(new ColorCube());     // 色付き立方体 (インデックス 1)
	BitSet flag=new BitSet(2);
	flag.set(0);    // 球を有効に
	flag.clear(1);  // 色付き立方体を無効に
	selecter.setChildMask(flag);

おわりに

Java3D の使い方を解説してきましたが、 いかがだったでしょうか。
 
SceneGraph とはどういうもので どうやって扱っていけばいいかさえ理解できていれば、 後は欲しい機能を実現するために どのノードを使ったらいいかをマニュアル片手に調べていけば やりたいことがやれるはずです。
 
最後に Java3D の情報が得られる Web ページを 紹介して終わりにしたいと思います。
 
この記事をきっかけに Java3D に興味を持っていただければ幸いです。
 
Java 3D(TM) API Home Page
http://java.sun.com/products/java-media/3D/index.html
Java3D の公式ページです。 ここからのランタイム、マニュアル、 仕様書がダウンロードできます。
 
Sun Microsystems Java 3D Computing Winners
http://www.sun.com/desktop/java3d/
Sun Microsystems, Inc. の Java3D に関する情報が集まっているページです。
 
Japanese Frequently Asked Questions about Java3D
http://tech.webcity.ne.jp/~andoh/java/3d/j3faq.html
Java3D の日本語版 FAQ です。
 
ABA Games
http://www.asahi-net.or.jp/~cs8k-cyu/
Java3D でつくったスペースバトルシューティングゲームや FAQ などがあります。
 
Java3DTips
http://www.ipc-tokai.or.jp/~atusi/java3d/index.shtml
Java3D の使い方について書かれています。

戻る