Java 3Dをはじめましょう

■Java 3Dの座標系は?

Java 3Dの座標系はどのようなものでしょうか。
簡単なサンプル・アプレットを見てください。

EarthApplet

このアプレットは、地球の中心がちょうど原点(x = 0, y = 0, z = 0)になっています。
右方向がX軸、上方向がY軸の正方向です。
Z軸の正方向は、地球の中心(原点)から目の位置(このアプレットを見ている皆さんの目です)に向かう方向になります。数学座標と同じですね。

この座標系のことを特に右手座標系と呼ぶことがあります。
目の前で右手の3本の指を広げてみてください。
右手の親指がX軸、人差し指がY軸、水平にした中指がZ軸というわけです。

Right Hand

Java 3Dでは使いませんが、「左手座標系」と呼ばれる座標系もあります。
左手座標系のX軸、Y軸は右手座標系と同じですが、Z軸の正負は右手座標系と逆になります。

Java 3Dの長さの単位はメートルです。1.0が1メートルです。

Java 3Dでは256ビット固定小数点座標が採用されています。
宇宙規模からプランク長までの仮想空間が表現できます。

2n メートル単位
87.29宇宙 (2,000万光年)
69.68銀河系 (100,000 光年)
53.07光年
43.43太陽系の直径
23.60地球の直径
10.65マイル
9.97キロメートル
0.00メートル
-19.93ミクロン
-33.22オングストローム
-115.57プランク長

Java 3Dの256ビット固定小数点座標を扱うためのオブジェクトとしてjavax.media.j3d.HiResCoordオブジェクトが用意されています。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.HiResCoord

クラス宣言

public class HiResCoord
extends java.lang.Object

HiResCoordオブジェクトには次のようなコンストラクターがあります。

public HiResCoord(int[] X, // X座標を表す、要素数8のintの配列
                  int[] Y, // Y座標を表す、要素数8のintの配列
                  int[] Z) // Z座標を表す、要素数8のintの配列

このコンストラクターでは、256ビット固定小数点値をあらわすために、要素数8のintの配列を使用します。

Java 3DTM API Specification の "3.5.3 Details of High-resolution Coordinates" での記述によると、
int配列の先頭要素(index 0)には、256ビット固定小数点数の最上位ビットが存在し、
int配列の最後の要素(index 7)には、256ビット固定小数点数の最下位ビットが存在し、
小数点位置は上位128ビットと下位128ビットの境界(index 3の要素とindex 4の要素の境界)だ、ということです。

■ウインドウの描画(通常のアプレット)

Java 3Dでの描画のためにアプレットを書いてみます。

Javaアプリケーションを書いても良いのですが、アプレットはAppletViewerやブラウザーで表示することも可能ですし、main()メソッドを書けばアプリケーションとして単独で実行することもできます。

FirstApplet.java
 1 // Java 3Dテスト用アプレット
 2 // FirstApplet.java
 3 
 4 import java.applet.*;
 5 
 6 public class FirstApplet extends Applet {
 7   public FirstApplet() {
 8   }
 9 }

これを表示するHTMLを書いてみます。

FirstApplet.html
 1 <HTML>
 2 <HEAD>
 3  <TITLE>Java 3D TestApplet</TITLE>
 4 </HEAD>
 5 <BODY>
 6 <APPLET code="FirstApplet.class"  width="500" height="500" codebase=".">
 7 </APPLET>
 8 </BODY>
 9 </HTML>

AppletViewerで実行してみましょう。

>appletviewer FirstApplet.html

FirstApplet.gif

このアプレットをJavaアプリケーションとしても実行できるようにmain()メソッドを書いてみましょう。

アプレットをアプリケーションとして実行させるには、main()メソッドのなかで最低限次のことを行う必要があります。

  1. そのアプレットのインスタンスを生成する
  2. java.awt.Frameのインスタンスを生成する
  3. Frameにアプレットのインスタンスをadd()する
  4. アプレットをinit(), start()させる
  5. Frameの幅、高さをsetSize()する
  6. Frameshow()する

Java 3Dにはこのような処理のための便利なクラスが用意されています。
com.sun.j3d.utils.applet.MainFrameクラスです。

■■com.sun.j3d.utils.applet.MainFrameクラス

com.sun.j3d.utils.applet.MainFrameクラスの場合、必要な処理はコンストラクターの引数にアプレットのインスタンスとFrameのサイズを指定するだけです。

FirstApplet.java
 1 // Java 3Dテスト用アプレット
 2 // FirstApplet.java
 3 
 4 import java.applet.*;
 5 import com.sun.j3d.utils.applet.MainFrame;...(1)
 6 
 7 public class FirstApplet extends Applet {
 8   public FirstApplet() {
 9   }
10   
11   public static void main(String[] args) {
12     FirstApplet applet = new FirstApplet(); .............(2)
13     MainFrame frame = new MainFrame(applet, 500, 500);...(3)
14   }
15 }

(1)でMainFrameクラスのimport宣言を行っています。

main()メソッドの中では、まず(2)でこのアプレット自身のインスタンスを生成しています。

(3)でcom.sun.j3d.utils.applet.MainFrameクラスのインスタンスを生成し、そのコンストラクターの引数としてFirstAppletアプレットのインスタンスとアプレットの幅、高さを与えています。

MainFrameにはいくつかコンストラクターがありますが、今回使ったものは次の仕様のものです。

public MainFrame(java.applet.Applet applet, // MainFrameに表示したいアプレットのインスタンス
                  int width,                 // アプレットの幅
                  int height)                // アプレットの高さ

このアプレットをJavaアプリケーションとして実行してみます。

>java FirstApptet

FirstApplet2.gif

MainFrameはjava.awt.Frameのサブクラスです。

クラス継承

java.lang.Object
  |
  +--java.awt.Component
        |
        +--java.awt.Container
              |
              +--java.awt.Window
                    |
                    +--java.awt.Frame
                          |
                          +--com.sun.j3d.utils.applet.MainFrame

クラス宣言

public class MainFrame
extends java.awt.Frame
implements java.lang.Runnable, java.applet.AppletStub, java.applet.AppletContext

コンストラクター

public MainFrame(java.applet.Applet applet, // アプレットのインスタンス
                 java.lang.String[] args,   // アプレットパラメーター
                 int width,                 // 幅
                 int height)                // 高さ

public MainFrame(java.applet.Applet applet, // アプレットのインスタンス
                 java.lang.String[] args)   // アプレットパラメーター

public MainFrame(java.applet.Applet applet, // アプレットのインスタンス
                 int width,                 // 幅
                 int height)                // 高さ

■Java 3Dのウインドウの描画(Canvas3D)

今までは通常のアプレットでした。これから少しずつJava 3Dの描画を追加していきます。

通常のJavaプログラムではjava.awt.Canvasを描画に使用しますが、Java 3Dでの描画にはjavax.media.j3d.Canvas3Dオブジェクトを使用します。

javax.media.j3d.Canvas3Djava.awt.Canvasのサブクラスです。

クラス継承

java.lang.Object
  |
  +--java.awt.Component
        |
        +--java.awt.Canvas
              |
              +--javax.media.j3d.Canvas3D

クラス宣言

public class Canvas3D
extends java.awt.Canvas

Canvas3Dのコンストラクターは以下の通りです。

public Canvas3D(java.awt.GraphicsConfiguration graphicsConfiguration)

引数はjava.awt.GraphicsConfigurationです。これは、現在のスクリーン解像度や色の数などのグラフィック・ディバイスの状況を抽象化したオブジェクトです。
この引数がnullの場合、javax.media.j3d.GraphicsConfigTemplate3Dオブジェクトの初期値の状態のGraphicsConfigurationが使用されます。
(Java 3DTM API Specification 8.9 "The Canvas3D Object", 8.9.3 "GraphicsConfig-Temp-late-3D Object" 参照)

Canvas3DFirstAppletに追加してみます。

FirstApplet.java
 1 // Java 3Dテスト用アプレット
 2 // FirstApplet.java
 3
 4 import java.applet.*;
 5 import java.awt.*;..........(1)
 6 import javax.media.j3d.*;...(2)
 7 import com.sun.j3d.utils.applet.MainFrame;
 8
 9 public class FirstApplet extends Applet {
10   public FirstApplet() {
11     Canvas3D canvas = new Canvas3D(null);....(3)
12     this.setLayout(new BorderLayout());......(4)
13     this.add(canvas, BorderLayout.CENTER);...(5)
14   }
15
16   public static void main(String[] args) {
17     FirstApplet applet = new FirstApplet();
18     MainFrame frame = new MainFrame(applet, 500, 500);
19   }
20 }

(1)ではレイアウトマネジャーjava.awt.BorderLayoutを使うためにjava.awtパッケージのimport宣言を行っています。
(2)ではCanvas3Dを使うためにjavax.media.j3dパッケージのimport宣言を行っています。

(3)ではCanvas3Dのインスタンスを生成しています。
引数はnullにしたので、GraphicsConfigurationはデフォルトのものが使われます。

(4)ではBorderLayoutのインスタンスを生成し、このアプレットのレイアウトマネジャーとしてsetLayout()しています。

(5)ではCanvas3Dをこのアプレットにadd()しています。
レイアウトマネジャーでの配置条件としてBorderLayout.CENTERを指定しているので、Canvas3Dは中央に配置されます。この場合、他にComponentが存在しないのでCanvas3Dはアプレットいっぱいに表示されます。

Canvas3Dを追加したアプレットを実行してみます。

>java FirstApptet

FirstApplet3.gif

Canvas3Dの追加前と特に変化はありません。

■SimpleUniverse

Java 3Dでの描画では、まずjavax.media.j3d.VirtualUniverseオブジェクトを生成する必要があります。
VirtualUniverseオブジェクトは、Java 3Dのシーン・グラフのすべての根(root)となる唯一のオブジェクトです。

VirtualUniverse.gif VirtualUniverseの生成

次に、VirtualUniverseを親オブジェクトとするjavax.media.j3d.Localeオブジェクトを生成する必要があります。
Localeオブジェクトは、VirtualUniverseオブジェクトのインスタンスをそのコンストラクターの引数にとります。

public Locale(VirtualUniverse universe)

createLocale.gifLocaleの生成

生成されたLocaleオブジェクトの座標はX = 0, Y = 0, Z = 0 (0, 0, 0)です。
Localeオブジェクトの座標系は256ビット固定小数点座標です。

Localeオブジェクトには、256ビット固定小数点でその座標を指定するコンストラクターも用意されています。

public Locale(VirtualUniverse universe, // 親となるVirtuualUniverseオブジェクト
              int[] x,                  // X座標を示す、要素数8のint配列
              int[] y,                  // Y座標を示す、要素数8のint配列
              int[] z)                  // Z座標を示す、要素数8のint配列

public Locale(VirtualUniverse universe, // 親となるVirtuualUniverseオブジェクト
              HiResCoord hiRes)         // このLocaleの座標となるjavax.media.j3d.HiResCoordオブジェクト

これらのコンストラクターを使えば、仮想空間の任意の位置の座標を指定してLocaleオブジェクトを生成することができます。

Localeオブジェクトには、2つの系統のツリーを追加する必要があります。

subgraphs.gif

ひとつは物体側のツリーです。自分で定義した多角形や、あらかじめJava 3Dで定義されている球体や立方体などの目に見える物体をJavaオブジェクトとして生成し、ツリー構造に構築して行きます。

一方、物体を見ている側の、目、耳などの身体、視点、スクリーンやグラフィックス装置などの「見る側」をJavaオブジェクトとして生成し、ツリー構造に構築したものが必要になります。
こちらを「View側のツリー」と呼ぶことにします。

このVirtualUniverse, Localeの生成と、View側のツリー構築をしてくれる便利なオブジェクトがあります。
com.sun.j3d.utils.universe.SimpleUniverseオブジェクトです。

SimpleUniverse.gif

SimpleUniverseVirtualUniverseのサブクラスです。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.VirtualUniverse
       |
       +--com.sun.j3d.utils.universe.SimpleUniverse

クラス宣言

public class SimpleUniverse
extends VirtualUniverse

FirstAppletSimpleUniverseを追加してみましょう。

FirstApplet.java
 1 // Java 3Dテスト用アプレット
 2 // FirstApplet.java
 3
 4 import java.applet.*;
 5 import java.awt.*;
 6 import javax.media.j3d.*;
 7 import com.sun.j3d.utils.applet.MainFrame;
 8 import com.sun.j3d.utils.universe.SimpleUniverse;...(1)
 9
10 public class FirstApplet extends Applet {
11   public FirstApplet() {
12     Canvas3D canvas = new Canvas3D(null);
13     this.setLayout(new BorderLayout());
14     this.add(canvas, BorderLayout.CENTER);
15
16     SimpleUniverse universe = new SimpleUniverse(canvas);...(2)
17   }
18
19   public static void main(String[] args) {
20     FirstApplet applet = new FirstApplet();
21     MainFrame frame = new MainFrame(applet, 500, 500);
22   }
23 }

(1)でSimpleUniverseimportを宣言しています。

(2)でSimpleUniverseのインスタンスを生成しています。
引数はCanvas3Dオブジェクトです。

ここで使用したSimpleUniverseのコンストラクターは次の通りです。

public SimpleUniverse(Canvas3D canvas)

SimpleUniverseを追加したアプレットを実行してみます。

>java FirstApptet

FirstApplet4.gif

背景が黒く塗りつぶされています。
SimpleUniverseの背景色の初期値は黒です。

SimpleUniverseには便利なメソッドがあります。

Canvas3Dの生成のとき、コンストラクターの引数のjava.awt.GraphicsConfigurationnullにしました。
SimpleUniverseにはシステムが持つ適切なGraphicsConfigurationを取得するメソッドがあります。

public static java.awt.GraphicsConfiguration getPreferredConfiguration()

staticが指定されていることで分かるように、getPreferredConfiguration()はクラスメソッドです。

このメソッドを使ってGraphicsConfigurationオブジェクトを取得し、それを引数にCanvas3Dを生成するように書き換えて見ましょう。

FirstApplet.java
 1 // Java 3Dテスト用アプレット
 2 // FirstApplet.java
 3 
 4 import java.applet.*;
 5 import java.awt.*;
 6 import javax.media.j3d.*;
 7 import com.sun.j3d.utils.applet.MainFrame;
 8 import com.sun.j3d.utils.universe.SimpleUniverse;
 9 
10 public class FirstApplet extends Applet {
11   public FirstApplet() {
12     GraphicsConfiguration config =
13       SimpleUniverse.getPreferredConfiguration();...(1)
14     Canvas3D canvas = new Canvas3D(config);.........(2)
15     this.setLayout(new BorderLayout());
16     this.add(canvas, BorderLayout.CENTER);
17 
18     SimpleUniverse universe = new SimpleUniverse(canvas);
19   }
20 
21   public static void main(String[] args) {
22     FirstApplet applet = new FirstApplet();
23     MainFrame frame = new MainFrame(applet, 500, 500);
24   }
25 }

(1)でGraphicsConfigurationオブジェクトを取得しています。

取得したGraphicsConfigurationオブジェクトを引数にして、(2)でCanvas3Dオブジェクトを生成しています。

実行結果は同じなので省略します。

■BranchGroup

SimpleUniverseを追加することでView側のツリーは構築できました。
ビジュアル・オブジェクトのツリーを構築するためにまず必要となるものは何でしょうか。

それがjavax.media.j3d.BranchGroupオブジェクトです。

BranchGroupオブジェクトはjavax.media.j3d.Groupのサブクラスです。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Group
                    |
                    +--javax.media.j3d.BranchGroup

クラス宣言

public class BranchGroup
extends Group

シーン・グラフを構成するオブジェクトは大きく2つに分かれます。

一つはGroup (javax.media.j3d.Group)オブジェクトです。
GroupオブジェクトはaddChild(javax.media.j3d.Node child)メソッドを持ち、javax.media.j3d.Node(とそのサブクラス)を“子供”として保持することができます。

group.gif

GroupオブジェクトにはBranchGroupのほかにjavax.media.j3d.TransformGroup, javax.media.j3d.Switch, javax.media.j3d.OrderedGroupなどがあります。

もう一つはLeaf (javax.media.j3d.Leaf)オブジェクトです。
Leafオブジェクトは他のjavax.media.j3d.Nodeオブジェクトを“子供”として保持することはできません。

leaf.gif

Leafオブジェクトにはjavax.media.j3d.Shape3Dなどがあります。

BranchGroupSimpleUniverseに追加するために、SimpleUniverseにはaddBcanchGraph(javax.media.j3d.BranchGroup)メソッドがあります。

public void addBranchGraph(BranchGroup bg)

ではFirstAppletBranchGroupを加えてみましょう。

FirstApplet.java
 1 // Java 3Dテスト用アプレット
 2 // FirstApplet.java
 3 
 4 import java.applet.*;
 5 import java.awt.*;
 6 import javax.media.j3d.*;
 7 import com.sun.j3d.utils.applet.MainFrame;
 8 import com.sun.j3d.utils.universe.SimpleUniverse;
 9 
10 public class FirstApplet extends Applet {
11   public FirstApplet() {
12     GraphicsConfiguration config =
13       SimpleUniverse.getPreferredConfiguration();
14     Canvas3D canvas = new Canvas3D(config);
15     this.setLayout(new BorderLayout());
16     this.add(canvas, BorderLayout.CENTER)
17 
18     SimpleUniverse universe = new SimpleUniverse(canvas);
19 
20     BranchGroup scene = new BranchGroup();...(1)
21 
22     universe.addBranchGraph(scene);..........(2)
23   }
24 
25   public static void main(String[] args) {
26     FirstApplet applet = new FirstApplet();
27     MainFrame frame = new MainFrame(applet, 500, 500);
28   }
29 }

(1)でBranchGroupのインスタンスを生成しています。

生成したBranchGroupのインスタンスを、(2)でSimpleUniverseaddBcanchGraph()しています。

これから追加する描画処理は、すべてこのBranchGroupaddChild()していくことになります。

今後描画処理が増えていくことを考慮して、BranchGroupの生成とシーン・グラフ構築の部分をコンストラクターとは別のメソッドとして独立させることにします。
これをcreateSceneGraph()メソッドとしてまとめてみます。

FirstApplet.java
 1 // Java 3Dテスト用アプレット
 2 // FirstApplet.java
 3 
 4 import java.applet.*;
 5 import java.awt.*;
 6 import javax.media.j3d.*;
 7 import com.sun.j3d.utils.applet.MainFrame;
 8 import com.sun.j3d.utils.universe.SimpleUniverse;
 9 
10 public class FirstApplet extends Applet {
11   public FirstApplet() {
12     GraphicsConfiguration config =
13       SimpleUniverse.getPreferredConfiguration();
14     Canvas3D canvas = new Canvas3D(config);
15     this.setLayout(new BorderLayout());
16     this.add(canvas, BorderLayout.CENTER);
17 
18     SimpleUniverse universe = new SimpleUniverse(canvas);
19 
20     BranchGroup scene = createSceneGraph();
21 
22     universe.addBranchGraph(scene);
23   }
24 
25   private BranchGroup createSceneGraph() {...(1)
26     BranchGroup root = new BranchGroup();....(2)
27     
28     return root;.............................(3)
29   }
30 
31   public static void main(String[] args) {
32     FirstApplet applet = new FirstApplet();
33     MainFrame frame = new MainFrame(applet, 500, 500);
34   }
35 }

(1)からcreateSceneGraph() メソッドの定義が始まります。
引数は無し、戻り値はBranchGroupのインスタンスです。

(2)でBranchGroupのインスタンスを生成し、(3)でそのインスタンスを戻り値としてreturnしているだけです。
今後、描画処理を追加する場合、この(2)と(3)の間に追加していくことになります。

BranchGroupaddBranchGraph()したあとのシーン・グラフは次のようになります。

BranchGraph.gif

ではFirstAppletを実行してみましょう。

>java FirstApplet

FirstApplet4.gif

描画処理を何も追加していないので特に変化はありません。

■プリミティブの描画(Primitive)

このアプレットに目に見える物体を追加してみましょう。

Java 3Dにはいくつかのオブジェクトがテストのために用意されています。

そうしたオブジェクトのうち、ここではcom.sun.j3d.utils.geometry.ColorCubeを使ってみます。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Leaf
                    |
                    +--javax.media.j3d.Shape3D
                          |
                          +--com.sun.j3d.utils.geometry.ColorCube

クラス宣言

public class ColorCube
extends Shape3D

ColorCubeは各面の色が違う立方体です。
ColorCubeはテスト用に作られたサンプルです。
実用的価値(?)は特にありません。

FirstApplet.java
 1 // Java 3Dテスト用アプレット
 2 // FirstApplet.java
 3 
 4 import java.applet.*;
 5 import java.awt.*;
 6 import javax.media.j3d.*;
 7 import com.sun.j3d.utils.applet.MainFrame;
 8 import com.sun.j3d.utils.universe.SimpleUniverse;
 9 import com.sun.j3d.utils.geometry.ColorCube;
10 
11 public class FirstApplet extends Applet {
12   public FirstApplet() {
13     GraphicsConfiguration config =
14       SimpleUniverse.getPreferredConfiguration();
15     Canvas3D canvas = new Canvas3D(config);
16     this.setLayout(new BorderLayout());
17     this.add(canvas, BorderLayout.CENTER);
18 
19     SimpleUniverse universe = new SimpleUniverse(canvas);
20 
21     BranchGroup scene = createSceneGraph();
22 
23     universe.addBranchGraph(scene);
24   }
25 
26   private BranchGroup createSceneGraph() {
27     BranchGroup root = new BranchGroup();
28     
29     root.addChild(new ColorCube(0.4));...(1)
30     
31     return root;
32   }
33 
34   public static void main(String[] args) {
35     FirstApplet applet = new FirstApplet();
36     MainFrame frame = new MainFrame(applet, 500, 500);
37   }
38 }

(1)でColorCubeのインスタンスを生成し、BranchGroupaddChild()しています。

ColorCubeのコンストラクターの引数は、ColorCubeの大きさです。
この場合、ColorCubeは原点(0, 0, 0)を中心にX, Y, Z各軸に0.4の大きさを持つ立方体になります。

colorcubesize.gif

ここで使用したColorCubeのコンストラクターは以下の通りです。

public ColorCube(double scale)

ではFirstAppletを実行してみましょう。

>java FirstApplet

FirstApplet4.gif

相変わらず何も表示されませんね。

実はこれは視点位置が原点(0, 0, 0)にあるために、ColorCubeと重なっていて何も見えないのです。

ColorCubeが見えるように、視点を適当な位置に移動するコードを追加してみます。

FirstApplet.java
 1 // Java 3Dテスト用アプレット
 2 // FirstApplet.java
 3 
 4 import java.applet.*;
 5 import java.awt.*;
 6 import javax.media.j3d.*;
 7 import com.sun.j3d.utils.applet.MainFrame;
 8 import com.sun.j3d.utils.universe.SimpleUniverse;
 9 import com.sun.j3d.utils.geometry.ColorCube;
10 
11 public class FirstApplet extends Applet {
12   public FirstApplet() {
13     GraphicsConfiguration config =
14       SimpleUniverse.getPreferredConfiguration();
15     Canvas3D canvas = new Canvas3D(config);
16     this.setLayout(new BorderLayout());
17     this.add(canvas, BorderLayout.CENTER);
18 
19     SimpleUniverse universe = new SimpleUniverse(canvas);
20     universe.getViewingPlatform().setNominalViewingTransform();...(1)
21 
22     BranchGroup scene = createSceneGraph();
23 
24     universe.addBranchGraph(scene);
25   }
26 
27   private BranchGroup createSceneGraph() {
28     BranchGroup root = new BranchGroup();
29 
30     root.addChild(new ColorCube(0.4));
31 
32     return root;
33   }
34 
35   public static void main(String[] args) {
36     FirstApplet applet = new FirstApplet();
37     MainFrame frame = new MainFrame(applet, 500, 500);
38   }
39 }

(1)でSimpleUniversegetViewingPlatform()メソッドを使って、com.sun.j3d.utils.universe.ViewingPlatformオブジェクトを取得しています。
ViewingPlatformオブジェクトは視点を適当な位置に移動するメソッドを持っています。

public void setNominalViewingTransform()

setNominalViewingTransform()メソッドは視点位置をZ軸方向に約2.41移動させます。

ではFirstAppletを実行してみましょう。

>java FirstApplet

FirstApplet9.gif

ColorCubeの視点に近い面が見えるようになりました。

ColorCubeを追加したことで、シーングラフは次のようになりました。

addColorCube.gif

ColorCubeのほかの面を見るためには、ColorCubeを移動したり回転させたりする必要があります。
移動、回転、拡大縮小などの効果については後に解説します。

com.sun.j3d.utils.geometryパッケージにはColorCubeのほかにもいくつかのオブジェクトが定義されています。

クラス名説明サンプル
Box直方体BoxApplet.gif
ColorCube各面に色のついた立方体ColorCubeApplet.gif
Cone円錐ConeApplet.gif
Cylinder円柱CylinderApplet.gif
Sphere球体SphereApplet.gif

ColorCube以外のオブジェクトは、照明されないとうまく表示できません。
Java 3Dでの照明の方法については後に説明します。

はじめに紹介した地球のアプレットは、com.sun.j3d.utils.geometry.Sphereオブジェクトに地球の表面のグラフィックをテクスチャー・マッピングしたものです。
テクスチャー・マッピングとは、3次元のオブジェクトの表面に2次元のグラフィックを貼り付けたような効果が得られる技法のことです。

テクスチャー (JPEGファイル) テクスチャー・マッピングしたサンプル
earch.jpg earthapplet2.gif

テクスチャー・マッピングについても後に解説します。

■回転、移動、拡大縮小(TransformGroup)

今までのアプレットではColorCubeの赤い面しか見えませんでした。
他の面も見えるように、ColorCudeを回転させるにはどうしたら良いでしょうか。

このような回転、移動、拡大縮小などの操作のためのオブジェクトがjavax.media.j3d.TransformGroupオブジェクトです。
TransformGroupjavax.media.j3d.Groupのサブクラスです。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Group
                    |
                    +--javax.media.j3d.TransformGroup

クラス宣言

public class TransformGroup
extends Group

コンストラクター (一部)

public TransformGroup()

Groupのサブクラスなので、BranchGroupなどと同じくaddChild()メソッドを持ち、javax.media.j3d.Nodeオブジェクトのサブクラスを“子供”として保持することができます。

回転(Rotation)、移動(Translation)、拡大縮小(Scaling)などの操作を総称して「変換」(Transform)と呼びます。
TransformGroup に適用した変換操作は、TransformGroupaddChild() されたすべてのノードに対して適用されます。

FirstAppletTransformGroupを加えてみましょう。

FirstApplet.java
 1 // Java 3Dテスト用アプレット
 2 // FirstApplet.java
 3 
 4 import java.applet.*;
 5 import java.awt.*;
 6 import javax.media.j3d.*;
 7 import com.sun.j3d.utils.applet.MainFrame;
 8 import com.sun.j3d.utils.universe.SimpleUniverse;
 9 import com.sun.j3d.utils.geometry.ColorCube;
10 
11 public class FirstApplet extends Applet {
12   public FirstApplet() {
13     GraphicsConfiguration config =
14       SimpleUniverse.getPreferredConfiguration();
15     Canvas3D canvas = new Canvas3D(config);
16     this.setLayout(new BorderLayout());
17     this.add(canvas, BorderLayout.CENTER);
18 
19     SimpleUniverse universe = new SimpleUniverse(canvas);
20     universe.getViewingPlatform().setNominalViewingTransform();
21 
22     BranchGroup scene = createSceneGraph();
23 
24     universe.addBranchGraph(scene);
25   }
26 
27   private BranchGroup createSceneGraph() {
28     BranchGroup root = new BranchGroup();
29 
30     TransformGroup trans = new TransformGroup();...(1)
31     trans.addChild(new ColorCube(0.4));............(2)
32 
33     root.addChild(trans);..........................(3)
34 
35     return root;
36   }
37 
38   public static void main(String[] args) {
39     FirstApplet applet = new FirstApplet();
40     MainFrame frame = new MainFrame(applet, 500, 500);
41   }
42 }

(1)でTransformGroupのインスタンスを生成しています。

(2)でColorCubeのインスタンスを生成し、BranchGroupではなくTransformGroupaddChild()しています。

(3)ではTransformGroupBranchGroupaddChild()しています。

TransformGroupを追加したことで、シーン・グラフは次のようになります。

add TransformGroup.gif

まだ回転などの効果を加えていないので、実行結果は前と変わりません。

■■Transform3D

回転、移動、拡大縮小などの効果はTransformGroupに対して直接適用することはできません。
TransformGroupjavax.media.j3d.Transform3Dオブジェクトのインスタンスを保持しています。
回転、移動、拡大縮小などはこのTransform3Dに対して適用することになります。

Transform3Djava.lang.Objectの直接のサブクラスです。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.Transform3D

クラス宣言

public class Transform3D
extends java.lang.Object

コンストラクター (一部)

public Transform3D()

Transform3Dには回転、移動、拡大縮小のために次のようなメソッドが用意されています。

public void rotX(double angle)

  X軸を回転軸にしてangle(単位:ラジアン)だけ回転します。
  回転以外の操作(拡大縮小、移動)はリセットされます。

public void rotY(double angle)

  Y軸を回転軸にしてangle(単位:ラジアン)だけ回転します。
  回転以外の操作(拡大縮小、移動)はリセットされます。

public void rotZ(double angle)

  Z軸を回転軸にしてangle(単位:ラジアン)だけ回転します。
  回転以外の操作(拡大縮小、移動)はリセットされます。

public final void setRotation(javax.vecmath.AxisAngle4d a1)

  javax.vecmath.AxisAngle4d で指定されたベクトルを回転軸にして 
  AxisAngle4d で指定された角度(単位:ラジアン)だけ回転します。
  AxisAngle4dは次のようなコンストラクターを持つオブジェクトです。

    public AxisAngle4d(double x,     // 回転軸ベクトルのX成分
                       double y,     // 回転軸ベクトルのY成分
                       double z,     // 回転軸ベクトルのZ成分
                       double angle) // 回転角(ラジアン)


public final void setRotation(javax.vecmath.AxisAngle4f a1)

  javax.vecmath.AxisAngle4f で指定された軸を回転軸にして
  AxisAngle4f で指定された角度(単位:ラジアン)だけ回転します。
  AxisAngle4fは次のようなコンストラクターを持つオブジェクトです。

    public AxisAngle4f(float x,     // 回転軸ベクトルのX成分
                       float y,     // 回転軸ベクトルのY成分
                       float z,     // 回転軸ベクトルのZ成分
                       float angle) // 回転角(ラジアン)

public final void setTranslation(javax.vecmath.Vector3d trans)

  javax.vecmath.Vertor3d で指定された座標に移動します。
  Vector3dは次のようなコンストラクターを持つオブジェクトです。
  
    public Vector3d(double x, // X座標
                    double y, // Y座標
                    double z) // Z座標

public final void setTranslation(javax.vecmath.Vector3f trans)

  javax.vecmath.Vertor3d で指定された座標に移動します。
  Vector3fは次のようなコンストラクターを持つオブジェクトです。
  
    public Vector3f(float x, // X座標
                    float y, // Y座標
                    float z) // Z座標

public final void setScale(double scale)

  scaleだけ拡大縮小します。1より大きい値は拡大、1より小さい値のときは縮小します。

public final void setScale(javax.vecmath.Vector3d scale)

  javax.vecmath.Vector3d で指定されたX, Y, Z各軸の倍率で拡大縮小します。
  1より大きい値は拡大、1より小さい値のときは縮小します。

Transform3Dには、上記以外にも座標変換のためのメソッドが数多く定義されています。

■■回転

Transform3Dを使ってColorCubeを回転させてみましょう。

FirstApplet.java (一部)

27   private BranchGroup createSceneGraph() {
28     BranchGroup root = new BranchGroup();
29 
30     Transform3D t3d = new Transform3D();..............(1)
31     t3d.rotY(Math.PI / 4.0);..........................(2)
32 
33     TransformGroup trans = new TransformGroup(t3d);...(3)
34 
35     trans.addChild(new ColorCube(0.4));
36 
37     root.addChild(trans);
38 
39     return root;
40   }
41 
42   public static void main(String[] args) {
43     FirstApplet applet = new FirstApplet();
44     MainFrame frame = new MainFrame(applet, 500, 500);
45   }
46 }

(1)でTransform3Dのインスタンスを生成しています。

(2)でTransform3DrotY()メソッドを使って、Y軸を回転軸にしてπ/4ラジアンだけ回転しています。

(3)でTransformGroupのインスタンスを生成していますが、このときTransform3Dを引数にしています。
TransformGroupのコンストラクターにはTransform3Dを引数にするものもあります。

public TransformGroup(Transform3D t1)

このコンストラクターで生成されたTransformGroupには、引数のTransform3Dの座標変換が適用されます。

FirstAppletを実行してみましょう。

>java FirstApplet

FirstApplet11.gif

Y軸を回転軸にπ/4ラジアン回転したので、黄色い面が見えています。

回転方向はどのように考えれば良いのでしょうか。
回転方向を考えるとき重要なのは、回転軸のベクトルの向きです。

screw.gif

回転軸ベクトルの向きに、右ねじを回転させるところを想像してください。
このときの右ねじが回転する方向がTransform3Dの回転メソッドの回転方向になります。

このサンプルではY軸を回転軸にπ/4ラジアン回転させているので、赤い面の左側にあった黄色い面が見えています。

ここで重要なのは、Y軸を中心にして X軸、Z軸も回転している、という点です。

beforeRotate.gif afterRotate.gif

この図は斜め上から見ています。Y軸を中心に X,Z軸も回転しています。
たとえば、この後さらにrotX()で回転させるとします。すでに回転した座標系のX軸を中心に回転することになります。

ColorCubeをX軸でも回転させてみましょう。


27   private BranchGroup createSceneGraph() {
28     BranchGroup root = new BranchGroup();
29 
30     Transform3D t3d = new Transform3D();
31     t3d.rotY(Math.PI / 4.0);
32     t3d.rotX(Math.PI / 4.0);...(1)
33 
34     TransformGroup trans = new TransformGroup(t3d);
35 
36     trans.addChild(new ColorCube(0.4));
37 
38     root.addChild(trans);
39 
40     return root;
41   }

(1)でX軸を回転軸にπ/4ラジアン回転しています。

ではFirstAppletを実行してみましょう。

>java FirstApplet

FirstApplet12.gif

最後に適用したX軸の回転しか有効になっていません。

rotX(), rotY(), rotZ()による回転は、 それ以前に適用された回転操作を無効にします。
もしいくつかの変換操作を重ねて適用したければ、Transform3Dmul()メソッドを使って効果を乗算する必要があります。

public final void mul(double scalar)

  mul()を適用したTransform3DのX,Y,Z各軸に対して、
  scalerだけ拡大縮小の効果が乗算されます。

public final void mul(double scalar,
                      Transform3D t1)

  t1のX,Y,Z各軸に対してscalerだけ拡大縮小されたものが、
  mul()を適用したTransform3Dにセットされます。
  t1は変化しません。

public final void mul(Transform3D t1)

  mul()を適用したTransform3Dとt1を乗算したものが、
  mul()を適用したTransform3Dにセットされます。
  t1は変化しません。

public final void mul(Transform3D t1,
                      Transform3D t2)

  t1, t2を乗算したものが、
  mul()を適用したTransform3Dにセットされます。
  t1, t2は変化しません。

mul()メソッドを使って書き直してみましょう。


27   private BranchGroup createSceneGraph() {
28     BranchGroup root = new BranchGroup();
29 
30     Transform3D t3d = new Transform3D();
31     t3d.rotY(Math.PI / 4.0);
32 
33     Transform3D rotateX = new Transform3D();..........(1)
34     rotateX.rotX(Math.PI / 4.0);......................(2)
35 
36     t3d.mul(rotateX);.................................(3)
37 
38     TransformGroup trans = new TransformGroup(t3d);...(4)
39 
40     trans.addChild(new ColorCube(0.4));
41 
42     root.addChild(trans);
43 
44     return root;
45   }

(1)でX軸の回転のためのTransform3Dオブジェクトのインスタンスを生成しています。

(2)でrotX()メソッドを使って、X軸を回転軸にしてπ/4だけ回転しています。

(3)でt3dに対して、mul()メソッドを使ってrotateXに加えたX軸の回転を乗算しています。

(4)でY軸, X軸の回転が乗算されたTransform3DTransformGroupsetTransform()しています。

FirstAppletを実行してみましょう。

>java FirstApplet

FirstApplet13.gif

rotY.gif rotY-rotX.gif

rotY()メソッドが実行されると、Y軸を回転軸にしてπ/4ラジアン回転しています。
この時点でX軸、Z軸もπ/4ラジアン回転していることに注意してください。
次にrotX()メソッドが実行され、X軸を回転軸にしてπ/4だけ回転しています。

rotY()rotX()の順序を逆にすると、これと違った結果が得られます。

FirstApplet.java (一部)

27   private BranchGroup createSceneGraph() {
28     BranchGroup root = new BranchGroup();
29 
30     Transform3D t3d = new Transform3D();
31     t3d.rotX(Math.PI / 4.0);.................(1)
32 
33     Transform3D rotateY = new Transform3D();
34     rotateY.rotY(Math.PI / 4.0);
35 
36     t3d.mul(rotateY);........................(2)
37 
38     TransformGroup trans = new TransformGroup(t3d);
39 
40     trans.addChild(new ColorCube(0.4));
41 
42     root.addChild(trans);
43 
44     return root;
45   }

(1)でrotX()メソッドが実行され、X軸を回転軸にしてπ/4ラジアン回転します。

rotX.gif rotX-rotY.gif

(2)でrotateYが乗算されていますが、t3dにはすでにrotX()による回転が適用されています。
つまり、すでに傾いているY軸を回転軸にして、π/4だけ回転することになります。

実行結果は次のようになります。

FirstApplet14.gif

Transform3DにはrotX(), rotY() rotZ()以外にも回転のためのメソッドがあります。
setRotation(AxisAngle4d)メソッドは任意の軸を回転軸にして回転ができます。

FirstApplet.java (一部)

28   private BranchGroup createSceneGraph() {
29     BranchGroup root = new BranchGroup();
30 
31     Transform3D t3d = new Transform3D();
32     t3d.setRotation(new AxisAngle4d(0.57, 0.57, -0.57, Math.PI / 4.0));...(1)
33 
34     TransformGroup trans = new TransformGroup(t3d);
35     
36     trans.addChild(new ColorCube(0.4));
37 
38     root.addChild(trans);
39 
40     return root;
41   }

(1)でjavax.vecmath.AxisAngle4dのインスタンスを生成しています。
AxisAngle4dのコンストラクターでは、X = 0.57, Y = 0.57, Z = -0.57のベクトルを回転軸に、π/4ラジアン回転するように指定しています。
このAxisAngle4dのインスタンスを引数にして、Transform3DsetRotation()を使って回転させています。

この実行結果は次のようになります。

FirstApplet15.gif

■■移動

移動はTransform3DsetTranslation()メソッドを使用します。

FirstApplet.java (一部)

28   private BranchGroup createSceneGraph() {
29     BranchGroup root = new BranchGroup();
30 
31     Transform3D t3d = new Transform3D();
32     t3d.setTranslation(new Vector3d(0.0, 0.4, 0.0));........(1)
33     
34     Transform3D rotation = new Transform3D();
35     
36     rotation.setRotation(
37       new AxisAngle4d(0.57, 0.57, -0.57, Math.PI / 4.0));...(2)
38 
39     t3d.mul(rotation);......................................(3)
40 
41     TransformGroup trans = new TransformGroup(t3d);
42     
43     trans.addChild(new ColorCube(0.4));
44 
45     root.addChild(trans);
46 
47     return root;
48   }
49 
50   public static void main(String[] args) {
51     FirstApplet applet = new FirstApplet();
52     MainFrame frame = new MainFrame(applet, 500, 500);
53   }
54 }

(1)でjavax.vecmath.Vector3dオブジェクトのインスタンスを生成しています。
コンストラクターの引数はX = 0.0, Y = 0.4, Z = 0.0で、Y方向に0.4移動させています。
生成したVector3dのインスタンスを引数にしてTransform3DsetTranslation()を呼んでいます。

(2)で回転を加えています。

(3)で移動に回転を乗算しています。

この実行結果は次のようになります。

FirstApplet16.gif

ここでは示しませんが、移動と回転の順序を逆にすると、回転した軸に沿って移動します。

■■拡大縮小

X, Y, Zすべての軸に対して同じ倍率で拡大縮小するには、Transform3DsetScale(double scale)を使用します。

X, Y, Z各軸に異なった倍率を適用したい場合はsetScale(Vector3d scale)メソッドを使用します。

FirstApplet.java (一部)

28   private BranchGroup createSceneGraph() {
29     BranchGroup root = new BranchGroup();
30 
31     Transform3D t3d = new Transform3D();
32     t3d.setRotation(
33       new AxisAngle4d(0.57, 0.57, -0.57, Math.PI / 4.0));
34 
35     Transform3D scaling = new Transform3D();.........(1)
36     scaling.setScale(new Vector3d(0.2, 0.8, 1.8));...(2)
37 
38     t3d.mul(scaling);................................(3)
39 
40     TransformGroup trans = new TransformGroup(t3d);
41     
42     trans.addChild(new ColorCube(0.4));
43 
44     root.addChild(trans);
45 
46     return root;
47   }

(1)でjavax.vecmath.Vector3dのインスタンスを生成しています。
コンストラクターの引数はX = 0.2, Y = 0.8, Z = 1.8です。それぞれの軸に対してこの倍率が適用されます。
(2)でVector3dのインスタンスを引数にしてsetScale()メソッドを呼んでいます。

(3)で回転したTransform3Dに対して拡大縮小の効果を乗算しています。

この実行結果は次のようになります。

FirstApplet17.gif

回転と拡大縮小の順序を逆にすると、違った結果が得られます。

FirstApplet.java (一部)
 
28   private BranchGroup createSceneGraph() {
29     BranchGroup root = new BranchGroup();
30 
31     Transform3D t3d = new Transform3D();
32     t3d.setScale(new Vector3d(0.2, 0.8, 1.8));..............(1)
33 
34     Transform3D rotation = new Transform3D();
35     rotation.setRotation(
36       new AxisAngle4d(0.57, 0.57, -0.57, Math.PI / 4.0));...(2)
37 
38     t3d.mul(rotation);......................................(3)
39 
40     TransformGroup trans = new TransformGroup(t3d);
41     
42     trans.addChild(new ColorCube(0.4));
43 
44     root.addChild(trans);
45 
46     return root;
47   }

(1)で拡大縮小を行い、(2)で回転を行っています。

(3)ではすでに拡大縮小された軸に対して回転が乗算されます。
このとき各軸のスケールは1:1ではなくなっているために、回転は歪んだかたちで行われます。

この実行結果は次のようになります。

FirstApplet18.gif

■物体をマウスで回転/移動/ズームさせる -MouseBehaviorの簡単な使い方

TransformGroupTransform3D を使えば、物体を回転/移動/拡大縮小できることがわかりました。では、マウスをつかってこれらの操作を実現するにはどうしたら良いでしょうか。

Java 3Dには、イベント処理のためのクラスとして javax.media.j3d.Behavior が用意されています。マウス、キーボードなどによるイベント処理は、Behavior を継承したクラスを書いて実現します。Behavior については別項で詳しく説明します。

com.sun.j3d.utils.behaviors.mouse パッケージでは、マウスによる回転、移動、ズームなどのクラスが提供されています。

回転、移動、ズームなどのクラスは MouseBehavior クラスを継承しています。MouseBehavior クラスを継承したクラスを書いて、独自のマウス処理を実現することもできると思います。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Leaf
                    |
                    +--javax.media.j3d.Behavior
                          |
                          +--com.sun.j3d.utils.behaviors.mouse.MouseBehavior
                                |
                                |--com.sun.j3d.utils.behaviors.mouse.MouseRotate    // 回転
                                |
                                |--com.sun.j3d.utils.behaviors.mouse.MouseTranslate // 移動
                                |
                                +--com.sun.j3d.utils.behaviors.mouse.MouseZoom      // ズーム

クラス宣言

public abstract class MouseBehavior
extends Behavior

コンストラクター (一部)

public MouseBehavior(TransformGroup transformGroup) // 操作対象の TransformGroup

public MouseBehavior(int format) // 起動時の動作オプション

定数フィールド (一部)

public static final int MANUAL_WAKEUP // 起動をマニュアルで行う
public static final int INVERT_INPUT  // Transform を逆転する

TransformGroup型の引数をとるコンストラクターでは、操作対象となる TransformGroup を指定して MouseBehavior を生成します。

int型の引数をとるコンストラクターでは、動作オプションを指定して MouseBehaviorを生成します。動作オプションには MANUAL_WAKEUP, INVERT_INPUT があります。

MANUAL_WAKEUP を指定すると、マウス操作ではなく wakeup()メソッドを実行して MouseBehavior を起動します。

INVERT_INPUTを指定すると、マウスによる操作を逆転することができます。例えば、物体でなく視点を回転/移動させたいときに便利です。

MANUAL_WAKEUP, INVERT_INPUT の両方を指定したいときは | 演算子 (OR演算子) で OR をとった値を指定します。

MouseBehavior の使用方法は次のようになります。

  1. 移動対象の TransformGroup をコンストラクターで指定して MouseBehavior を生成する。
    (実際に生成するのはそのサブクラスの MouseRotate, MouseTranslate, MouseZoom)
  2. setSchedulingBounds() メソッドで、スケジューリングが有効となる領域 (javax.media.j3d.Bounds オブジェクト) を設定する
  3. シーン・グラフに addChild() する

KeyNavigator の使用方法と良く似ています。ここで重要なのは、MouseBehavior の適用対象となる TransformGroup は常に一つである という点です。

MouseBehavior では複数の物体をそれぞれ別個に操作することはできません。複数の物体を別々に操作したい場合には、後に説明する com.sun.j3d.utils.behaviors.picking パッケージを使用してください。

では実行結果を見てください。

MouseBehaviorTest.gif

マウスでのドラッグで地球を回転させたり、移動したり、ズームしたり出来ます。マウス・ドラッグによる操作は、画面内のどの部分をクリックしても開始できます。地球以外の部分をクリックしてドラッグを開始してみてください。

MouseRotate, MouseTranlate, MouseZoom での操作は次の通りです。

マウスボタン操作
左ドラッグ回転
中央ドラッグズーム (Z軸方向への移動)
右ドラッグ移動 (X軸、Y軸方向)

3ボタンマウスでない場合、 マウスボタンのエミュレーションは環境によって異なります。 UNIX 環境では X-Window System でのマウス・エミュレーションに依存します。 Windows 環境ではマウスの左ボタンと同時にキーボードの [Alt] キーを押すことで中ボタンをエミュレートできます。

このサンプルのソースは次の通りです。

MouseBehaviorTest.java (一部)

34    private BranchGroup createSceneGraph() {
35      BranchGroup root = new BranchGroup();
36      
37      TransformGroup trans = new TransformGroup();
38      trans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);--┬(1)
39      trans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);-┘
40  
41      BoundingSphere bounds = new BoundingSphere(new Point3d(), 100.0);...(2)
42      
43      MouseRotate rotator = new MouseRotate(trans);...(3)
44      rotator.setSchedulingBounds(bounds);............(4)
45      root.addChild(rotator);.........................(5)
46      
47      MouseTranslate translator = new MouseTranslate(trans);┐
48      translator.setSchedulingBounds(bounds);               ├(6)
49      root.addChild(translator);----------------------------┘
50      
51      MouseZoom zoomer = new MouseZoom(trans);┐
52      zoomer.setSchedulingBounds(bounds);     ├(7)
53      root.addChild(zoomer);------------------┘
54      
55      trans.addChild( new ColorCube(0.4) );
56      
57      root.addChild(trans);
58      
59      return root;
60    }
61  
62    public static void main(String[] args) {
63      MouseBehaviorTest applet = new MouseBehaviorTest();
64      Frame frame = new MainFrame(applet, 500, 500);
65    }
66  }

(1)では実行時の Transform3D の読み取り/書き込みのための capability bit を設定しています。capability bit については次の項目で説明します。

(2)ではMouseRotate, MouseTranslate, MouseZoom に対してスケジューリングが有効になる領域 BoundingSphere を生成しています。この領域は原点を中心とした半径 100.0 の球の内部になります。領域を定義するためのクラスである BoundingSphere などについても後で詳しく説明します。

(3)でMouseRotate を生成しています。適用対象は BranchGroup 直下の TransformGroup です。(4)でスケジューリング領域を設定し、(5)で addChild() しています。

MouseRotate と同様に、(6)で MouseTranslate、(7)で MouseZoom に対して生成、適用、BranchGroup への addChild() を行っています。

■■capability bit

マウス操作のサンプルでは、setCapability()メソッドを使って TransformGroupALLOW_TRANSFORM_READ, ALLOW_TRANSFORM_WRITE という定数値を設定しています。

37      TransformGroup trans = new TransformGroup();
38      trans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
39      trans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

setCapability() しないとどうなるでしょうか。setCapability() の行をコメントにして再コンパイルし、サンプルを実行してみます。

$ java MouseBehaviorTest
Exception occurred during Behavior execution:
javax.media.j3d.CapabilityNotSetException: Group: no capability to get transform
	at javax.media.j3d.TransformGroup.getTransform(TransformGroup.java:98)
	at com.sun.j3d.utils.behaviors.mouse.MouseRotate.processStimulus(Compiled Code)
	at javax.media.j3d.BehaviorScheduler.processBehaviors(Compiled Code)
	at javax.media.j3d.BehaviorScheduler.run(BehaviorScheduler.java:949)

javax.media.j3d.CapabilityNotSetExceptionが発生しました。回転/移動/ズームなどの操作は行われませんでした。

Java 3D では、実行時に設定値を変更する場合、事前に読み取り/書き込みを"許可"しなければならない場合があります。このためのメソッドが setCapability() メソッドです。

setCapability() メソッドで"許可"される必要のある値が読み取り/書き込みされるとき、capability bit と呼ばれるビット値が検査されます。

capability bit を設定しない属性値を実行時に変更することはできません。capability bit で許可されていない値が読み取り/書き込みされると、javax.media.j3d.CapabilityNotSetException が発生します。

属性の読み取り/書き込みの許可 (setCapability() メソッド)

これまで説明したように、Java 3D で設定値を変更する場合、あらかじめ"許可"を与えておかなければならない場合があります。

このためのメソッドが setCapability() です。

setjavax.media.SceneGraphObject のメソッドです。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject

クラス宣言

public abstract class SceneGraphObject
extends java.lang.Object

メソッド (一部)

public final void setCapability(int bit)

SceneGraphObject から派生したサブクラスはすべて setCapability() メソッドを持っています。

実行時に値を読み取り/書き込みする場合、使用するクラスの ALLOW_xxxx 定数フィールドを調べて、適切な capability bit を設定する必要があります。

■■領域クラス javax.media.j3d.Bounds

MouseBehavior のサンプルでは、MouseBehavior が作用する領域として BoundingSphere を指定しました。

Java 3Dには、適用の対象となる領域を設定しないと有効にならないオブジェクトがあります。イベント処理(javax.media.j3d.Behavior)、照明(javax.media.j3d.Light)、霧(javax.media.j3d.Fog)、背景(javax.media.j3d.Background)などです。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Leaf
                    |
                    |--javax.media.j3d.Behavior           // イベント処理
                    |     |
                    |     +--javax.media.j3d.Interpolator // アニメーションの処理
                    |
                    |--javax.media.j3d.Light              // 照明
                    |
                    |--javax.media.j3d.Fog                // 霧
                    |
                    +--javax.media.j3d.Background         // 背景

これらのオブジェクトに適用対象となる領域を設定するのは、不必要な処理を省いて実行時のパフォーマンスを上げるためです。範囲外のものを描画対象から外し、実行時の処理を軽減します。

また、Java 3Dの物体はそれぞれ領域を持っています。物体どうしが重なっているかどうか、物体が特定の領域に入っているか、マウス位置にある物体は何か、などを物体が占める領域で判定します。

領域クラスは抽象クラス javax.media.j3d.Bounds を基底クラスとして、箱状の領域(javax.media.j3d.BoundingBox)、球状の領域(javax.media.j3d.BoundingSphere)、面で囲まれた閉領域(javax.media.j3d.BoundingPolytope)などのクラスがあります。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.Bounds                 // 領域の抽象クラス
        |
        +--javax.media.j3d.BoundingBox      // 矩形領域
        |
        +--javax.media.j3d.BoundingSphere   // 球状の領域
        |
        +--javax.media.j3d.BoundingPolytope // 面で囲まれた閉領域

クラス宣言

public abstract class Bounds
extends java.lang.Object
implements java.lang.Cloneable

Boundsjavax.media.j3d.Nodeのサブクラスではありません。したがって、シーングラフに addChild() されることはありません。

コンストラクター

public Bounds()

Boundsのコンストラクターはデフォルトコンストラクター(引数なしのコンストラクター)だけです。

Boundsには、他の領域や点、(半)直線との交差を判定するための intersect() メソッドが用意されています。

メソッド public abstract boolean intersect(Point3d origin, // 通過座標とベクトルで指定した半直線 Vector3d direction) // との交差判定 public abstract boolean intersect(Point3d point) // 点との交差判定 public abstract boolean intersect(Bounds boundsObject) // 他の領域との交差判定 public abstract boolean intersect(Bounds[] boundsObjects) // 他の複数領域との交差判定

例えば「マウスでクリックした座標から画面奥方向に延びる(半)直線」と「物体の領域」との交差を判定できれば、マウスクリックした物体を選択することもできるでしょう。

Bounds オブジェクトでの領域はローカル座標に従います。たとえば、TransformGroupaddChild() された物体が回転/移動/拡大縮小したとき、Boundsもその TransformGroup の回転/移動/拡大縮小に従います。

■■javax.media.j3d.BoundingBox

javax.media.j3d.BoundisgBox は箱状の領域を定義します。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.Bounds
        |
        +--javax.media.j3d.BoundingBox

クラス宣言

public class BoundingBox
extends Bounds

コンストラクター

public BoundingBox()

public BoundingBox(Point3d lower,       // 最も小さい位置座標
                   Point3d upper)       // 最も大きい位置座標

public BoundingBox(Bounds boundsObject) // Bounds オブジェクト

public BoundingBox(Bounds[] bounds)     // Bounds オブジェクト配列

コンストラクター引数として、最も小さい位置座標と、最も大きい位置座標の2つのPoint3d を与える場合はどのように考えたら良いでしょうか。

これまでのサンプルの ColorCubeのように、原点を中心に x, y, z方向それぞれ 0.4 の大きさを持った領域を考えてみます。

colorcubesize.gif

x, y, zそれぞれが最も小さい値である座標は (-0.4, -0.4, -0.4) です。それぞれが最も大きい値である座標は (0.4, 0.4, 0.4) です。BoundingBox は、この2点間の箱状の領域になります。

引数なしのコンストラクターで生成した BoundingBox のデフォルト領域は、最小が (-1.0, -1.0, -1.0)、最大が (1.0, 1.0, 1.0)の座標間の箱状の領域です。

他のBoundsオブジェクトや、Boundsオブジェクト配列を引数に与えるコンストラクターでは、引き数に指定したBoundsオブジェクトを囲むような新たな領域が設定されます。

■■javax.media.j3d.BoundingSphere

javax.media.j3d.BoundingSphere は中心点と半径を指定して球状の領域を定義します。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.Bounds
        |
        +--javax.media.j3d.BoundingSphere

クラス宣言

public class BoundingSphere
extends Bounds

コンストラクター

public BoundingSphere()

public BoundingSphere(Point3d center,         // 球の中心座標
                      double radius)          // 球の半径

public BoundingSphere(Bounds boundsObject)    // Bounds オブジェクト

public BoundingSphere(Bounds[] boundsObjects) // Bounds オブジェクト配列

引数を2つとるコンストラクターでは、Point3d で中心座標を、double で半径を指定して BoundingSphere を生成します。

引数なしのコンストラクターで生成した BoundingSphere の領域は、中心が(0.0, 0.0, 0.0)、半径が 1.0 の球状の領域になります。

BoundingBoxと同様に、他のBoundsオブジェクトや、Boundsオブジェクト配列を引数に与えるコンストラクターでは、引き数に指定したBoundsオブジェクトを囲むような新たな領域が設定されます。

■■javax.media.j3d.BoundingPolytope

javax.media.j3d.BoundingPolytope は複数の平面で空間を分割し、その内部の閉じた空間によって領域を定義します。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.Bounds
        |
        +--javax.media.j3d.BoundingPolytope

クラス宣言

public class BoundingPolytope
extends Bounds

コンストラクター

public BoundingPolytope()

public BoundingPolytope(Vector4d[] planes)      // 平面方程式の配列

public BoundingPolytope(Bounds boundsObject)    // Bounds オブジェクト

public BoundingPolytope(Bounds[] boundsObjects) // Bounds オブジェクト配列

平面は Vector4d を使った平面方程式で定義します。平面方程式は次の通りです。

Ax + By + Cz + D = 0

平面方程式はどのように考えれば良いでしょうか。

平面は、面に垂直なベクトルを使って定義できます。

plane_and_vector.gif

平面方程式の A, B, C 各係数が、ベクトルの x, y, z各成分になる、と考えても良いでしょう。

残る項Dはどのように考えたら良いでしょうか。

plane_formula.gif

Dは面から原点(0, 0, 0)までの距離と考えることができます。ベクトルの正方向がDの符号の正方向です。

平面方程式の A, B, C, DVector4dx, y, z, w 各成分に設定します。

クラス継承

java.lang.Object
  |
  +--javax.vecmath.Tuple4d
        |
        +--javax.vecmath.Vector4d

クラス宣言


public class Vector4d
extends Tuple4d
implements java.io.Serializable

コンストラクター (一部)

public Vector4d(double x, // x 成分
                double y, // y 成分
                double z, // z 成分
                double w) // w 成分

BoundingPolytope では、次の式で半空間を定義します。

Ax + By + Cz + D <= 0

無限に広がる空間は図に描けないので、羊羹や豆腐のように切り取った部分空間で図解してみます。

slice.gif

羊羹のような箱型の部分空間を、平面 Ax + By + Cz + D = 0 で切ったらどうなるでしょうか。

half_plane.gif

Ax + By + Cz + D <= 0 で定義された半空間は、切り取った平面を含んで、ベクトルと反対方向に広がる半空間です。図では羊羹の下半分がある半空間です。

half_plane2.gif

残りの半空間 Ax + By + Cz + D > 0 は、平面を含まず、ベクトルと同じ方向に広がる半空間です。図では羊羹の上半分がある半空間です。

引数無しのコンストラクターで生成された BoundingPolytope には、要素数6のVector4d 配列による平面方程式が設定されます。

Vector4d[]平面方程式半空間 Ax+By+Cz+D<=0
planes[0](1,0,0,-1)plane_plus_x.gif
planes[1](-1,0,0,-1)plane_minus_x.gif
planes[2](0,1,0,-1)plane_plus_y.gif
planes[3](0,-1,0,-1)plane_minus_y.gif
planes[4](0,0,1,-1)plane_plus_z.gif
planes[5](0,0,-1,-1)plane_minus_z.gif

これは、引数無しのコンストラクターで生成された BoundingBox と同じ領域です。

BoundingPolytopeでは3つ以上の平面で領域を指定します。Vector4d配列の要素数が3よりも小さいときは java.lang.IllegalArgumentException が発生します。

BoundingBox, BoundingSphereと同様に、他のBoundsオブジェクトや、Boundsオブジェクト配列を引数に与えるコンストラクターでは、引き数に指定したBoundsオブジェクトを囲むような新たな領域が設定されます。

■SmallUniverse を書いてみる

これまで、View側のツリーを構築するために SimpleUniverse を使って来ました。では SimpleUniverse で構築されたView側のツリーとはどのようなものなのでしょうか。

SimpleUniverse をはじめとする com.sun.j3d.utils.universe パッケージのソースは公開されていますので、読んでみると参考になると思います。

ここでは SimpleUniverse の代わりとなる、最も単純なクラスを書いてみます。

SimpleUniverse の代わりになるようなクラス、SmallUniverse を書いてみましょう。ソースは次のようになりました。

SmallUniverse.java
 1  // Java 3D Test Program
 2  // SmallUniverse.java
 3  // Copyright(c) 1999 ENDO Yasuyuki <yasuyuki@javaopen.org>
 4  
 5  import javax.media.j3d.*;
 6  
 7  public class SmallUniverse {
 8    protected VirtualUniverse universe = null;
 9    protected Locale locale = null;
10    protected BranchGroup root = null;
11    protected TransformGroup trans = null;
12    protected ViewPlatform vp = null;
13    protected View view = null;
14    protected PhysicalBody body = null;
15    protected PhysicalEnvironment env = null;
16    protected Canvas3D canvas = null;
17    
18    public SmallUniverse() {
19      this( new Locale( new VirtualUniverse() ) );
20    }
21    
22    public SmallUniverse(Locale locale) {
23      universe = locale.getVirtualUniverse();
24  
25      this.locale = locale;
26  
27      root = new BranchGroup();
28      root.setCapability(BranchGroup.ALLOW_DETACH);
29      root.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
30  
31      trans = new TransformGroup();
32      trans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
33      trans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
34      root.addChild(trans);
35  
36      vp = new ViewPlatform();
37      float r = vp.getActivationRadius();//DEBUG
38      System.out.println("r=" + r);//DEBUG
39      trans.addChild(vp);
40  
41      view = new View();
42      view.attachViewPlatform(vp);
43  
44      body = new PhysicalBody();
45      view.setPhysicalBody(body);
46  
47      env = new PhysicalEnvironment();
48      view.setPhysicalEnvironment(env);
49      
50      canvas = new Canvas3D(null);
51      view.addCanvas3D(canvas);
52  
53      this.locale.addBranchGraph(root);
54    }
55    
56    public VirtualUniverse getUniverse() { return universe; }
57  
58    public Locale getLocale() { return locale; }
59    //public void setLocale(Locale locale) { this.locale = locale; }
60  
61    public BranchGroup getBranchGroup() { return root; }
62  
63    public TransformGroup getTransformGroup() { return trans; }
64  
65    public void getTransform(Transform3D t3d) { trans.getTransform(t3d); }
66    public void setTransform(Transform3D t3d) { trans.setTransform(t3d); }
67    
68    public ViewPlatform getViewPlatform() { return vp; }
69  
70    public View getView() { return view; }
71  
72    public PhysicalBody getPhysicalBody() { return body; }
73    
74    public PhysicalEnvironment getPhysicalEnvironment() { return env; }
75  
76    public Canvas3D getCanvas() { return canvas; }
77    //public void setCanvas3D(Canvas3D canvas) { this.canvas = canvas; }
78  
79    public void detachBranchGraph() {
80      java.util.Enumeration e = locale.getAllBranchGraphs();
81      while (e.hasMoreElements()) {
82        BranchGroup g = (BranchGroup)e.nextElement();
83        if (g == root) locale.removeBranchGraph(root);
84      }
85    }
86    
87    public void atachBranchGraph() {
88      java.util.Enumeration e = locale.getAllBranchGraphs();
89      while (e.hasMoreElements()) {
90        BranchGroup g = (BranchGroup)e.nextElement();
91        if (g == root) return;
92      }
93      locale.addBranchGraph(root);
94    }
95    
96  }

SmallUniverse では VirtulalUniverse を生成して Locale を一つ追加し、この Locale の配下に View 側のツリーを構築していくことにします。

■■javax.media.j3d.VirtualUniverse

VirtualUniverse.gif

VirtualUniverse のコンストラクターは引数無しのものだけです。

コンストラクター

public VirtualUniverse()

VirtualUniverse には Locale を追加するためのメソッドはありません。 追加はLocale のコンストラクターで行います。

VirtualUniverse は複数の Locale を持つこともできます。

■■javax.media.j3d.Locale

javax.media.j3d.Locale はかならず一つの VirtualUniverse を"親"に持ちます。

createLocale.gif

コンストラクター (一部)

public Locale(VirtualUniverse universe) // 親となる VirtualUniverse

コンストラクター引数に VirtualUniverse を指定することで、VirtualUniverseLocale の"親"になります。

同じ VirtualUniverse をコンストラクター引数に指定して複数の Locale を生成したときには、VirtualUniverse が複数の Locale を保持することになります。

SmallUniverse では、VirtualUniverse型、Locale 型の変数を持たせてコンストラクター引数で初期化するようにしました。

SmallUniverse.java (一部)
 1  // Java 3D Test Program
 2  // SmallUniverse.java
 3  // Copyright(c) 1999 ENDO Yasuyuki <yasuyuki@javaopen.org>
 4  
 5  import javax.media.j3d.*;
 6  
 7  public class SmallUniverse {
 8    protected VirtualUniverse universe = null;
 9    protected Locale locale = null;
        :
        :
18    public SmallUniverse() {
19      this( new Locale( new VirtualUniverse() ) );
20    }
21    
22    public SmallUniverse(Locale locale) {
23      universe = locale.getVirtualUniverse();
24  
25      this.locale = locale;
          :
          :
50    }

引数なしのコンストラクターでは、新たに生成した VirtualUniverse を引数にして Locale を生成し、Locale を引数にとるコンストラクターを実行しています。

Locale を引数にとるコンストラクターでは、Locale#getVirtualUniverse() で取得した VirtualUniverse を変数 universe に代入し、引数として渡された Locale を変数 locale に代入しています。

引数無しのコンストラクターで SmallUnivrese を構築した場合には、新たな VirtualUniverse, Locale が生成されます。
Locale を引数にとるコンストラクターで生成された場合には、すでに生成された VirtualUniverse, Locale を保持します。

■■View側のBranchGraph

追加した Locale に View側の BranchGraph を構築して行きます。

Locale に追加できるのは javax.media.j3d.BranchGroup だけです。Locale には複数の BranchGroup を追加することができます。

Locale のメソッド (一部)

public void addBranchGraph(BranchGroup branchGroup)    // 追加したい BranchGroup

public void removeBranchGraph(BranchGroup branchGroup) // 削除したい BranchGroup

public void replaceBranchGraph(BranchGroup oldGroup,   // 置き換たい古い BranchGrop
                               BranchGroup newGroup)   // 新たな BranchGroup

public int numBranchGraphs()                           // BranchGroup の数を返す

public java.util.Enumeration getAllBranchGraphs()      // すべての BranchGroup を取得

SmallUniverseBranchGroup型、TransformGroup型の変数を持たせ、コンストラクターで初期化することにしました。

SmallUniverse.java (一部)
 7  public class SmallUniverse {
 8    protected VirtualUniverse universe = null;
 9    protected Locale locale = null;
10    protected BranchGroup root = null;
11    protected TransformGroup trans = null;
        :
        :
22    public SmallUniverse(Locale locale) {
23      universe = locale.getVirtualUniverse();
24  
25      this.locale = locale;
26  
27      root = new BranchGroup();
28  
29      trans = new TransformGroup();
30      trans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
31      trans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
32      root.addChild(trans);
          :
          :
50    }

この TransformGroup に設定されている Transform3DgetTransform() メソッドで取得できるようにしておきます。

59    public void getTransform(Transform3D t3d) { trans.getTransform(t3d); }

viewBranch.gif

この TransformGroup には、次に説明する ViewPlatformaddChild() します。

ViewPlatform の"親"である TransformGroup から取得した Transform3D に移動や回転を与えると、視点の移動や回転が実現できます。

■■javax.media.j3d.ViewPlatform

javax.media.j3d.ViewPlatform は View側のツリーの唯一の Leaf ノードです。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Leaf
                    |
                    +--javax.media.j3d.ViewPlatform

クラス宣言

public class ViewingPlatform
extends BranchGroup

コンストラクター

public ViewPlatform()

ViewPlatformaddChild() されている TransformGroup に移動、回転などの操作を与えると、視点を移動したり回転させたりすることができます。

SmallUniverse では ViewPlatform型の変数を宣言し、コンストラクターで生成して TransformGroupaddChild() しています。

11    protected TransformGroup trans = null;
         :
         :
34      vp = new ViewPlatform();
35      trans.addChild(vp);

addViewPlatform.gif

SmallUniverse では設定しませんでしたが、ViewPlatformには照明やアニメーションなどを活性化(Activation)させる領域を設定することができます。

メソッド (一部)

public final void setActivationRadius(float activationRadius) // 活性化領域の半径

setActivationRadius() メソッドで視野(ViewPlatform)の周囲の半径を指定します。デフォルト値は 62.0f です。

照明やアニメーションなどの Bounds (領域)が視野の周囲の領域と交差すると、<照明やアニメーションなどが有効になります。
(正確には有効化(活性化)の対象になります。実際に有効になるかどうかはこれだけでは決定できません)

■■javax.media.j3d.View

javax.media.j3d.Viewは描画に関する様々な情報を保持するためのクラスです。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.View

クラス宣言

public class View
extends java.lang.Object

コンストラクター

public View()

クラス継承で分かるように、View はシーングラフに追加されません。attachViewPlatform() メソッドで設定された ViewPlatform を参照します。

メソッド (一部)

public final void attachViewPlatform(ViewPlatform vp) // 参照するViewPlatform

addView.gif

SmallUniverse では次のように View を生成して attachViewPlatform()ViewPlatform を設定しました。

13    protected View view = null;
         :
         :
41      view = new View();
42      view.attachViewPlatform(vp);

View は様々なオブジェクト、設定を保持します。

ビューポリシーSCREEN_VIEW
投影ポリシーPERSPECTIVE_PROJECTION
スクリーンスケールポリシーSCALE_SCREEN_SIZE
ウインドウリサイズポリシーPHYSICAL_WORLD
ウインドウ移動ポリシーPHYSICAL_WORLD
ウインドウ視点ポリシーRELATIVE_TO_FIELD_OF_VIEW
単一視的ビューポリシーCYCLOPEAN_EYE_VIEW
フロントクリップポリシーPHYSICAL_EYE
バッククリップポリシーPHYSICAL_EYE
視覚ポリシーVISIBILITY_DRAW_VISIBLE
互換モードfalse
左の投影行列identity
右の投影行列identity
ViewPlatform座標から視野座標への変換行列identity
PhysicalBodyオブジェクトnull
PhysicalEnvironmentオブジェクトnull
スクリーンスケール1.0
視野角PI/4
フロントクリップ距離0.1
バッククリップ距離10.0
トラッキング可能false
ユーザー頭部から仮想世界への変換を可能にするfalse
Canvas3Dオブジェクトのリストempty
透明度描画のときに深度バッファーを無視するかどうかtrue
スクリーンアンチエリアスfalse
照明を左右の目の位置で計算するfalse
ViewPlatformオブジェクトnull
behaviorスケジューラーが実行中かどうかtrue
描画を実行するtrue

SmallUniverseでは最低限必要なものだけを設定し、あとはデフォルト値のまま使用しました。ここで行った設定は次のものです。

attachViewPlatform() についてはすでに説明しました。

setPhysicalBody() はヘッドマウントディスプレイなどを装着した人間の目や耳の位置などを定義するためのオブジェクト PhysicalBody を設定します。

setPhysicalEnvironment() はセンサーや入力機器についての情報を定義するためのオブジェクト PhysicalEnvironment を設定します。

addCanvas3D()Canvas3D を追加します。複数の Canvas3D を追加できます。

■■javax.media.j3d.PhysicalBody

javax.media.j3d.PhysicalBodyは、人間の目や耳の位置などを定義します。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.PhysicalBody

クラス宣言

public class PhysicalBody
extends java.lang.Object

コンストラクター

public PhysicalBody()

public PhysicalBody(Point3d leftEyePosition,  // 左目の位置
                    Point3d rightEyePosition) // 右目の位置

public PhysicalBody(Point3d leftEyePosition,  // 左目の位置
                    Point3d rightEyePosition, // 右目の位置
                    Point3d leftEarPosition,  // 左耳の位置
                    Point3d rightEarPosition) // 右耳の位置

コンストラクターでは人間の目の位置、耳の位置などを設定することができます。この位置は頭部座標系での位置です。頭部座標系の原点は両目の中間です。

目と耳の位置以外にも、目と地表の距離や、目とスクリーンの距離などを設定することができます。引数無しのコンストラクターで生成されたときのデフォルト値は次のようになります。

左目の位置(-0.033, 0.0, 0.0)
右目の位置(0.033, 0.0, 0.0)
左耳の位置(-0.080, -0.030, 0.095)
右耳の位置(0.080, -0.030, 0.095)
目と地表の距離1.68
目とスクリーンの距離0.4572
頭部座標系からヘッドトラッカー座標系への変換Transform3D単位行列

ヘッドマウントディスプレイを使った場合に、頭部の動きを捕捉して視点位置を変更する必要があります。頭部座標系から、ヘッドトラッカー座標系への変換のための Transform3D を設定することもできます。

メソッド (一部)

public void setHeadToHeadTracker(Transform3D t) // 頭部座標→ヘッドトラッカー座標変換のための Transform3D

SmallUniverse では引数無しのコンストラクターで生成したデフォルト値のまま使用しました。

14    protected PhysicalBody body = null;
         :
         :
44      body = new PhysicalBody();
45      view.setPhysicalBody(body);

addPhysicalBody.gif

■■javax.media.j3d.PhysicalEnvironment

javax.media.j3d.PhysicalEnvironmentは頭部や腕などに付けたセンサーや、サウンドに関する設定値を定義します。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.PhysicalEnvironment

クラス宣言

public class PhysicalEnvironment
extends java.lang.Object

コンストラクター (一部)

public PhysicalEnvironment()

SmallUniverse では引数無しのコンストラクターで生成した PhysicalEnvironmentをそのまま使用しました。

15    protected PhysicalEnvironment env = null;
         :
         :
47      env = new PhysicalEnvironment();
48      view.setPhysicalEnvironment(env);

addPhysicalEnvironment.gif

PhysicalEnvironmentには、頭部や腕などのセンサーや、サウンドのための様々な値を設定できます。

引数無しのコンストラクターで生成された PhysicalEnvironment には次のようなデフォルト値が設定されています。

センサーの数3
配列変数sensorsnull (for all array elements)
頭部のindex0
右手のindex1
左手のindex2
利き腕のindex1
利き腕でない腕のindex2
トラッキング可能false
オーディオディバイスnull
オーディオディバイスのリストempty
物理共存座標系からトラッカーベース座標系への変換identity
物理空間における物理共存座標系ポリシーView.NOMINAL_SCREEN

■■javax.media.j3d.Canvas3D

javax.media.j3d.Canvas3D は3次元空間を2次元のウインドウ(スクリーン)に描画する機能を持ちます。また、描画のための様々な値を設定することができます。

クラス継承

java.lang.Object
  |
  +--java.awt.Component
        |
        +--java.awt.Canvas
              |
              +--javax.media.j3d.Canvas3D

クラス宣言

public class Canvas3D
extends java.awt.Canvas

コンストラクター

public Canvas3D(java.awt.GraphicsConfiguration graphicsConfiguration) // グラフィックス状況

public Canvas3D(java.awt.GraphicsConfiguration graphicsConfiguration, // グラフィックス状況
                boolean offScreen)                                    // オフスクリーン描画をする/しない

Canvas3D のコンストラクターでは、現在のグラフィックス状況を表す java.awt.GraphicsConfiguration オブジェクトを引数にとります。

二つの引数をとるコンストラクターでは、オフスクリーン描画の有無を設定します。デフォルト値は falseです。

通常は目に見える描画画面(スクリーン)に描画されます。オフスクリーンを有効にすると、スクリーンではなくメモリーバッファー(オフスクリーンバッファー)に描画します。バッファーに描画された画像をスクリーンに転送することもできますし、ファイル保存したり、印刷したり、加工して利用することもできます。

SmallUniverseでは、java.awt.GraphicsConfigurationだけを引数にとるコンストラクターで生成した Canvas3D をデフォルト値のまま使用しました。

16    protected Canvas3D canvas = null;
         :
         :
50      canvas = new Canvas3D(null);
51      view.addCanvas3D(canvas);

addCanvas3D.gif

引数無しのコンストラクターでCanvas3Dを生成したときのデフォルト値は次のようになります。

イメージプレート上の左目位置(0.142, 0.135, 0.4572)
イメージプレート上の右目位置(0.208, 0.135, 0.4572)
立体視の有効/無効true
ダブルバッファーの有効/無効true
単一視ビューポリシーView.CYCLOPEAN_EYE_VIEW
オフスクリーンモードfalse
オフスクリーンバッファーnull
オフスクリーン位置(0,0)

イメージプレートとは何でしょうか。

Canvas3D は、3次元空間上のものを2次元の"板"に投影して描画します。この仮想的な"板"のことをイメージプレートと呼んでいます。

imageplate.gif

Viewのデフォルト値では、目からスクリーンまでの距離は 0.4572 でした。イメージプレートはこのスクリーン位置にある"板"です。

3次元空間上の物体はイメージプレートに投影されます。

eye_view.gif

イメージプレートに投影された画像がスクリーンに描画されます。

■SimpleUniverse あれこれ

■■SimpleUniverse で表示できる範囲

com.sun.j3d.utils.universe.SimpleUniverse を使ったサンプルでは setNominalViewingTransform() メソッドで、視点をZ軸方向に約2.41移動させました。また、描画される物体は ±1.0 の範囲に収まるような大きさにしていました。

SimpleUniverse で描画できる範囲はどこからどこまでなのでしょうか? setNominalViewingTransform() メソッドは何を行っているのでしょうか?

View オブジェクトのデフォルト値では、視野の角度(field of view)は π/4ラジアン(45°)です。

視野の前後についても限界があります。視点からの距離が 0.1 よりも近くにある物体は描画されません。また、視点からの距離が 10.0 を超える物体も描画されません。

視野の手前側の描画の限界距離はフロントクリップ距離(flont clip distance)、視野の奥の描画の限界距離はバッククリップ距離(back clip distance)と呼ばれています。

視野の角度と前方、後方のクリップ距離から、図のような角錐台の領域以外は表示されないことがわかるでしょう。
(実際には、視点から0.4572の距離にイメージプレートがあるので、イメージプレートより近くにある物体は描画されません)

viewing_volume.gif

大きい物体を表示させるときには表示領域を広げる必要があります。表示領域を広げるにはどうしたら良いでしょうか?

最も手軽なのはバッククリップ距離を大きくすることです。View にはこのためのメソッドとして setBackClipDistance() があります。

public final void setBackClipDistance(double distance) // バッククリップ距離

com.sun.j3d.utils.universe.SimpleUniverse を使う場合には次のようにします。

  1. SimpleUniverse#getViewer() メソッドで com.sun.j3d.utils.universe.Viewer オブジェクトを取得する
  2. Viewer#getWiew()メソッドで javax.media.j3d.View オブジェクトを取得する
  3. View#setBackClipDistance() メソッドでバッククリップ距離を設定する

この操作は次のように書くこともできます。

simpleUniverse.getViewer().getView().setBackClipDistance(100.0); // 100.0までを表示

■■setNominalViewingTransform()では何を行っているのか?

com.sun.j3d.utils.universe.SimpleUniverse には getViewingPlatform() というメソッドがあります。

public ViewingPlatform getViewingPlatform()

getViewingPlatform() で取得できるオブジェクトは com.sun.j3d.utils.universe.ViewingPlatform です。名前が似ていますが、javax.media.j3d.ViewPlatform とは別のオブジェクトです。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Group
                    |
                    +--javax.media.j3d.BranchGroup
                          |
                          +--com.sun.j3d.utils.universe.ViewingPlatform

クラス宣言

public class ViewingPlatform
extends BranchGroup

com.sun.j3d.utils.universe.ViewingPlatformには、Z軸方向に視点を移動するためのsetNaminalViewingTransform() メソッドがあります。

public void setNominalViewingTransform()

setNominalViewingTransform() メソッドでは何を行っているのでしょうか?

setNominalViewingTransform() メソッドでは、次のような計算式でZ軸方向への移動距離を計算しています。

Z軸移動距離 = 1.0 / Math.tan(視野角/2.0);

視野の角度の1/2のtanの逆数を求めています。原点位置±1.0の範囲のものがちょうど視野に入る距離です。

viewDistance.gif

視野の角度が π/4 ラジアンのとき、Z軸移動距離は約 2.41 になります。

■■SimpleUniverse から Locale を取り出す

com.sun.j3d.utils.universe.SimpleUniverse には Locale を取得するための getLoale() があります。

public Locale getLocale() // Locale を取得

BranchGroup を追加するには SimpleUniverse#addBranchGraph() メソッドを使えば良いのですが、SimpleUniverse には追加したBranchGroupを削除するメソッドはありません。

SimpleUniverse から BranchGroup を削除するにはつぎのような処理が必要になります。

  1. getLocale()Locale を取得する
  2. Locale#removeBranchGraph() メソッドで BranchGroup を削除する
SimpleUniverse universe = new SimpleUniverse(canvas);

BranchGroup scene = new BranchGroup();
scene.setCapability(BranchGroup.ALLOW_DETACH); // 実行時の remove を許可する
universe.addBranchGraph(scene);
  :
  :
universe.getLocale().removeBranchGraph(scene);

実行時に Locale から BranchGroup を削除するには BranchGroup.ALLOW_DETACH という capability bit を設定する必要があります。

■■SimpleUniverse の視点側の TransformGroup を取り出す

視点を移動させたいときなど、SimpleUniverse から視点側の TransformGroup を取り出したい場合があります。

視点側の TransformGroupcom.sun.j3d.utils.universe.ViewingPlatform が保持しています。

ViewingPlatform のメソッド (一部)

public TransformGroup getViewPlatformTransform() // 視点側の TransformGroup を取得する

SimpleUniverse には ViewingPlatform を取得するためのメソッド getViewingPlatform() があります。視点側の TransformGroup を取得するには次のような処理が必要になります。

  1. SimpleUniverse#getVieiwingPlatform メソッドで ViewingPlatform を取得する
  2. ViewingPlatform#getViewPlatformTransform() メソッドで視点側の TransformGroup を取得する
SimpleUniverse universe = new SimpleUniverse(canvas);
  :
  :
TransformGroup vtrans = universe.getViewingPlatform().getViewPlatformTransform();

取得した TransformGroup に対して移動、回転などの変換操作を行うと視点が移動、回転します。

■シーングラフを図に描いてみよう -Java 3D のシーングラフ表記法-

いままでシーングラフを図に描いてきましたが、このへんでシーングラフの表記法についてまとめてみましょう。

j3dscene.gif

Java 3Dのオブジェクトの多くは javax.media.j3d.SceneGraphObject を継承しています。

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
        |     |
        |     +--javax.media.j3d.Group
        |     |
        |     +--javax.media.j3d.Leaf
        |
        +--javax.media.j3d.NodeComponent
              |
              +--javax.media.j3d.Geometry
              |
              +--javax.media.j3d.Appearance

SceneGraphObject のサブクラス javax.media.j3d.Node には Group, Leaf というサブクラスがあります。

■■親子関係

Group には addChild(Node) メソッドがあり、他の Group, Leaf を"子供"として保持することができます。ツリー状のシーングラフはこの"親子関係"によって構築されます。

parent_child.gif

親子関係は実線矢印で表します。向きは親→子です。

"親"ノードに加えられた操作は、その"親"の配下の"子供"ノードに影響します。たとえば TransformGroup に加えられた変換操作はその配下のすべての"子供"ノードに影響します。

■■オブジェクト参照

NodeComponentSceneGraphObject のサブクラスですが、こちらはノードとしてツリーを構築することはありません。他のオブジェクトから参照されるだけです。

reference.gif

シーングラフの図ではオブジェクト参照は点線矢印で表します。

■■VirtuelUniverse

VirtualUniverse は斜めに線が入った偏平な楕円で表します。

VirtualUniverse.gif

この図は次のようなコードに対応しています。

VirtualUniverse universe = new VirtualUniverse();

■■Locale

Locale は菱形で表します。

createLocale.gif

この図は次のようなコードに対応しています。

Locale locale = new Locale(universe);

■■Group

Groupノードは円で表します。

group_node.gif

円の中に Group クラスの頭文字を書きます。例えば BranchGroupなら BGTransformGroupならTです。

■■Leaf

Leafノードは三角形で表します。

leaf_node.gif

三角形の中に Leaf クラスの頭文字を書きます。例えば ViewPlatform なら VP です。

■■NodeComponent

NodeComponent は楕円で表します。

node_component.gif

楕円の中に NodeComponent クラスのクラス名や頭文字を書きます。

■■Behavior

BehaviorLeaf のサブクラスなので、三角形の中に頭文字 B を書いて表します。

behavior_node.gif

■■その他のクラス

SceneGraphObject のサブクラスでないその他のクラスは四角形で表します。

other_class.gif

四角形の中や、四角形の下などにクラス名を書きます。

■javax.media.j3d.SceneGraphObject

javax.media.j3d.SceneGraphObjectはシーングラフを構成するオブジェクトの基底クラスです。capability bit を設定したり、任意のユーザーデータを設定するメソッドがあります。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject

クラス宣言

public abstract class SceneGraphObject
extends java.lang.Object

コンストラクター
public SceneGraphObject()

ScereGraphObject は抽象クラスです。サブクラスには NodeNodeComponent があります。このうち Node はツリー状のシーングラフを構成します。NodeComponent はツリーの構成要素にはなりませんが、様々な値が設定され Node から参照されます。

capability bits全 bit クリア
live 状態かどうかfalse
compiled 状態かどうかfalse
ユーザー定義オブジェクトnull

SceneGraphObject には capability bit を設定/取得するためのメソッドがあります。

public final void setCapability(int bit) // capability bit 整数定数
public final boolean getCapability(int bit) // capability bit 整数定数

capability bit の設定は、その ScereGraphObject を含むツリーがシーングラフに追加される前に行う必要があります。

capability bit で設定する定数値は、クラスごとに定義されています。

public final void clearCapability(int bit) // capability bit 整数定数

clearCapability() は定数で指定された capability bit をクリアします。

public final boolean isCompiled() // compile() されているかどうか

isCompiled() は、そのSceneGraphObject"compile"状態かどうかを返します。

BranchGroup には compile() というメソッドがあります。BranchGroupcompile()するとそのBranchGroup 配下のツリーは最適化されますが、ノードを動的に追加/削除することなどができなくなります。

public final boolean isLive() // "live"状態かどうか

isLive()は、その SceneGraphObject"live" 状態かどうかを返します。

BranchGroupLocaleaddBranchGraph() されると、その BranchGroup 配下のツリーは Java 3D の描画スケジューリングの対象になります。ツリーが描画スケジューリングの対象になっていることを "live" 状態と呼びます。

public void setUserData(java.lang.Object userData) // 任意のユーザー定義オブジェクト
public java.lang.Object getUserData()              // 任意のユーザー定義オブジェクトを取得

setUserData(), getUserData() は任意のユーザー定義オブジェクトを設定/取得します。

設定したユーザー定義オブジェクトは描画には直接関係ありませんが、SceneGraphObject に任意の情報を設定することができます。

■javax.media.j3d.Node

javax.media.j3d.Nodeはシーングラフの"節"(ノード)としてツリー状のシーングラフを構築します。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node

クラス宣言

public abstract class Node
extends SceneGraphObject

コンストラクター

public Node()

シーングラフは、addChild() メソッドで追加された Node の親子関係によって構築されます。

ピッキング可能true
衝突検知対象になるtrue
boundsを自動計算true
boundsなし (自動計算)
public final Node getParent()

getParanet() メソッドはその Node の"親" Node を取得します。

public final void setBounds(Bounds bounds)
public final Bounds getBounds()

setBounds(), getBounds()メソッドは、その Node の境界領域を設定/取得します。設定した領域 (javax.media.j3d.Boundsオブジェクト)は、他の Node との衝突判定(交差判定)、照明、スケジューリングの有効範囲などに使われます。

public void setCollidable(boolean collidable)
public boolean getCollidable()

setCollidable(), getCollidable() は、衝突判定の対象にするかどうかを設定/取得します。setCollidable(false)と設定した場合、この物体は衝突判定の対象でなくなります。つまり、この物体に衝突しても衝突は検知されなくなります。

public final void setBoundsAutoCompute(boolean autoCompute)
public final boolean getBoundsAutoCompute()

setBoundsAutoCompute(), getBoundsAutoCompute()は、この NodeBoundsを自動計算するかどうかを設定します。デフォルト値は true です。この Node"live" にならないと自動計算は行われません。

public final void getLocalToVworld(Transform3D t) // 座標変換
public final void getLocalToVworld(SceneGraphPath path, // この Node を含むパス
                                   Transform3D t)       // 変換座標

getLocalToVWorld() メソッドは、この Node のローカル座標系から、バーチャルワールド座標系への変換行列を Transform3D に取得します。

SceneGraphPath を引数に取るものは、この Node が含まれる SceneGraphPath を引数に指定します。

public Node cloneTree()

public Node cloneTree(boolean forceDuplicate)            // 複製を強制するかどうか

public Node cloneTree(boolean forceDuplicate,            // 複製を強制するかどうか
                      boolean allowDanglingReference)    // ダングリング参照の許可するかどうか

public Node cloneTree(NodeReferenceTable referenceTable) // ノード参照テーブル

public Node cloneTree(NodeReferenceTable referenceTable, // ノード参照テーブル
                      boolean forceDuplicate)            // 複製を強制するかどうか

public Node cloneTree(NodeReferenceTable referenceTable, // ノード参照テーブル
                      boolean forceDuplicate,            // 複製を強制するかどうか
                      boolean allowDanglingReferences)   // ダングリング参照を許可するかどうか

cloneTree() メソッドは、この Node 以下のすべてのツリーを複製します。

forceDuplicatetrueを指定すると、Node が参照している NodeComponent も常に複製されます。これが false のときは各 NodeComponentduplicateOnCloneTree フラグが検査され、duplicateOnCloneTreefalse のときは NodeComponent は複製されません。

public Node cloneNode(boolean forceDuplicate) // 複製を強制するかどうか
cloneNode() メソッドは、この Node の新たな複製を作成します。

forceDuplicatetrueを指定すると、Node が参照している NodeComponent も常に複製されます。これが false のときは各 NodeComponentduplicateOnCloneTree フラグが検査され、duplicateOnCloneTreefalse のときは NodeComponent は複製されません。

public void duplicateNode(Node originalNode,      // 複製元の Node
                          boolean forceDuplicate) // 複製を強制するかどうか

duplicateNode() メソッドは、引数に指定された Node をこの Node に複製します。

forceDuplicatetrueを指定すると、Node が参照している NodeComponent も常に複製されます。これが false のときは各 NodeComponentduplicateOnCloneTree フラグが検査され、duplicateOnCloneTreefalse のときは NodeComponent は複製されません。

public void setPickable(boolean pickable)
public boolean getPickable()

setPickable()メソッドは、この Node へのピッキングを可能にしたり不可能にしたりします。getPickable() メソッドはピッキングが可能かどうかを取得します。マウスなどで物体を選択することをピッキングと呼びます。デフォルト値では true です。

■javax.media.j3d.NodeComponent

javax.media.j3d.NodeComponentNode から参照され、様々な属性を設定するために使用されます。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.NodeComponent

クラス宣言

public abstract class NodeComponent
extends SceneGraphObject

コンストラクター

public NodeComponent()

public final void setDuplicateOnCloneTree(boolean duplicate) // 複製するかどうか
public final boolean getDuplicateOnCloneTree()

setDuplicateOnCloneTree(),getDuplicateOnCloneTree()メソッドは、cloneTree() メソッドが実行されたとき、この NodeComponent を複製するかどうかを設定/取得します。true のときは複製されます。false のときは複製されませんが、cloneTree() の引数の forceDuplicatetrue にしたときはこのフラグは無視され常に複製されます。

public NodeComponent cloneNodeComponent(boolean forceDuplicate)

cloneNodeComponent() メソッドはこの NodeComponent から新たな複製を作成します。

引数の forceDupcicatetrue のとき、この NodeComponentduplicateOnCloneTree フラグは無視され、常に複製されます。forceDuplicatefalse のときは duplicateOnCloneTreeフラグが検査され、duplicateOnCloneTreetrue のときだけ複製されます。

public void duplicateNodeComponent(NodeComponent originalNodeComponent, // 複製元の NodeComponent
                                   boolean forceDuplicate)              // 複製を強制するかどうか

duplicateNodeComponent() メソッドは、引数で指定された NodeComponent をこの NodeComponent に複製します。

引数の forceDupcicatetrue のとき、引数に指定された NodeComponentduplicateOnCloneTree フラグは無視され、常に複製されます。forceDuplicatefalse のときは duplicateOnCloneTreeフラグが検査され、duplicateOnCloneTreetrue のときだけ複製されます。

■Groupノードのいろいろ

Groupのサブクラス

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Group
                    |
                    +--javax.media.j3d.BranchGroup
                    |
                    +--javax.media.j3d.OrderedGroup
                    |
                    +--com.sun.j3d.utils.geometry.Primitive
                    |
                    +--javax.media.j3d.SharedGroup
                    |
                    +--javax.media.j3d.Switch
                    |
                    +--javax.media.j3d.TransformGroup

BranchGroup, TransofrmGroup 以外の Group ノードについて簡単に説明します。

■javax.media.j3d.Group

GroupaddChild()メソッドを持ち、他の Node をその"子供"として保持します。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Group

クラス宣言

public class Group
extends Node

コンストラクター

public Group()
public final void setCollisionBounds(Bounds bounds) // 衝突境界領域を取得
public final Bounds getCollisionBounds()

setCollisionBounds(),getCollisionBounds() メソッドは、この Group の衝突境界領域を設定/取得します。

public final void setChild(Node child, // セットしたい Node
                           int index)  // Node の index

setChild() メソッドは、指定された indexNode をセットします。index0 以上、numChildren() より小です。

Java 3D 1.2 以降では引数 childnull を指定できます。

public final void insertChild(Node child, // 挿入したい Node
                              int index)  // Node の index

insertChild() メソッドは、指定された index の"位置"に Node を挿入します。index0 以上、numChildren() より小です。

Java 3D 1.2 以降では引数 childnull を指定できます。

public final void removeChild(int index) // Node の index

removeChild() メソッドは、指定された indexNode を削除します。index0 以上、numChildren() より小です。

public final Node getChild(int index) // Node の index

getChild() メソッドは、指定された indexNode を取得します。

public final java.util.Enumeration getAllChildren() // すべての"子"を取得

getAllChildren() メソッドは、すべての Node を取得します。

public final void addChild(Node child) // 追加したい Node

addChild() メソッドは、引数に指定された NodeGroup に"子"として追加します。

Java 3D 1.2 以降では引数 childnull を指定できます。

public final void moveTo(BranchGroup branchGroup) // この Group の移動先の BranchGroup

moveTo() メソッドは、引数に指定された BranchGroup を、それまで接続されていた Group から 切り離し、この GroupaddChild() します。

public final int numChildren() // "子"の数を取得

numChildren() メソッドは、"子"Nodeの数を取得します。

public final void setAlternateCollisionTarget(boolean target)
public final boolean getAlternateCollisionTarget()

setAlternateCollisionTarget()は、このGroupが衝突相手になったときに、衝突を報告させるかどうかを設定します。デフォルトでは false です。

public Node cloneNode(boolean forceDuplicate)

cloneNode() はこの Groupノードを複製します。

引数の forceDupcicatetrue のとき、この NodeComponentduplicateOnCloneTree フラグは無視され、常に複製されます。forceDuplicatefalse のときは duplicateOnCloneTreeフラグが検査され、duplicateOnCloneTreetrue のときだけ複製されます。

■javax.media.j3d.Switch

javax.media.j3d.Switch は、追加した"子"ノードのそれぞれについて、描画の対象にするか、描画対象から外すかを設定できる Group ノードです。

クラス継承
java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Group
                    |
                    +--javax.media.j3d.Switch

クラス宣言

public class Switch
extends Group

コンストラクター

public Switch()

public Switch(int whichChild)             // 表示させる"子"の index

public Switch(int whichChild,             // 表示させる"子"の index
              java.util.BitSet childMask) // マスク値

定数フィールド (一部)

public static final int CHILD_NONE // 全てを表示する
public static final int CHILD_ALL  // 全て表示しない
public static final int CHILD_MASK // indexまたはマスク値で指定した"子"だけを表示

Switch は、特定の"子"を描画対象にしたり、描画の対象から外したりできます。描画対象から外されたノードは、シーンに存在しなくなる、と言っても良いかも知れません。

int 型の引数をとるコンストラクターでは、whitchChild に指定した index の"子"ノードだけが描画の対象になります。witchChild には CDILD_NONE, CHILD_ALL, CHILD_MASK を指定することもできます。witchChildのデフォルト値は CHILD_NONE です。

CHLID_NONE を指定するとすべての"子"ノードは描画されません。

CHILD_ALL を指定するとすべての"子"ノードが描画されます。

CHILD_MASK を指定したときは、java.util.BitSet によるマスク値で指定した"子"ノードだけが描画されます。

public final void setWhichChild(int child)                 // 表示させる"子"の index

setWhitchClild()メソッドは、引数 childで指定された index の"子"ノードを描画の対象に設定します。指定された"子"以外は描画の対象から外されます。

public final void setChildMask(java.util.BitSet childMask) // マスク値

setChildMask() メソッドは、java.util.BitSet によるマスク値で指定された"子"ノードだけを描画対象に設定します。このメソッドを有効にするには、setWhitchChild() メソッドで CHILD_MASK を指定しておく必要があります。

■javax.media.j3d.SharedGroup

javax.media.j3d.SharedGroupjavax.media.j3d.Link から参照され、複数のシーングラフで共有することができる特殊な Group ノードです。

SharedGroupそれ自体を他の Group に追加することはできませんLink から参照されるだけです。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Group
                    |
                    +--javax.media.j3d.SharedGroup

クラス宣言

public class SharedGroup
extends Group

コンストラクター

public SharedGroup()

SharedGroup は他のGroup と同様に、他のノードを"子"として保持できます。ただし SharedGroup を別の GroupaddChild() することはできません。

SharedGroup は、次で説明する Link から参照され、複数のシーングラフから共有されます。

■■javax.media.j3d.Link

javax.media.j3d.LinkSharedGroup を参照する Leaf ノードです。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Leaf
                    |
                    +--javax.media.j3d.Link

クラス宣言

public class Link
extends Leaf

コンストラクター

public Link()

public Link(SharedGroup sharedGroup) // 参照する SharedGroup

引数を持つコンストラクターでは、この Link が参照する SharedGroup を指定します。

SharedGroup をその配下に addChild() したいシーングラフでは、Link を生成して SharedGroup を参照させ、その Link をシーングラフに追加します。

shared_group.gif
SharedGroup はどの Group にも addChild() しない

public void setSharedGroup(SharedGroup sharedGroup) // 参照する SharedGroup
public SharedGroup getSharedGroup()                 // 参照する SharedGroup を取得

setSharedGroup()メソッドは、この Link が参照する SharedGroup を設定します。

getSharedGroup() メソッドは、この Link が参照している SharedGroup を取得します。

■javax.media.j3d.OrderedGroup

javax.media.j3d.OrderedGroup は、追加されたノードが追加された順番で描画されることを保証する Group ノードです。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Group
                    |
                    +--javax.media.j3d.OrderedGroup

クラス宣言

public class OrderedGroup
extends Group

コンストラクター

public OrderedGroup()

OrderedGroup に追加されたノードは、追加された順番に描画されます。

通常 Java 3Dの描画スケジューリングでは、何がいつ描画されるかはプログラマーには分かりません。透明度の描画のときなどには奥の物体から順番に描画して行かなければならない場合があります。このような描画の順序によって結果が異なる場合に OrderedGroup を使用します。

■javax.media.j3d.Leaf

javax.media.j3d.Leaf は配下に"子"を持たないノードのための抽象クラスです。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Leaf

クラス宣言

public abstract class Leaf
extends Node

コンストラクター

public Leaf()

Leaf は独自のメソッドを持ちません。

Leaf には次のような直接のサブクラスがあります。

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Leaf
                    |
                    +--javax.media.j3d.AlternateAppearance
                    |
                    +--javax.media.j3d.Background // 背景
                    |
                    +--javax.media.j3d.Behavior
                    |
                    +--javax.media.j3d.BoundingLeaf
                    |
                    +--javax.media.j3d.Clip
                    |
                    +--javax.media.j3d.Fog        //  霧
                    |
                    +--javax.media.j3d.Light      // 光源
                    |
                    +--javax.media.j3d.Link
                    |
                    +--javax.media.j3d.ModelClip
                    |
                    +--javax.media.j3d.Morph
                    |
                    +--javax.media.j3d.Shape3D
                    |
                    +--javax.media.j3d.Sound
                    |
                    +--javax.media.j3d.Soundscape
                    |
                    +--javax.media.j3d.ViewPlatform