Mon, 06 Mar 2006
Sun, 19 Feb 2006
IndexedGeometryArray で TEXTURE_COORDINATE_2を指定する場合
テクスチャーに使う画像は Appearance の setTexture で与える。
IndexedGeometryArray の中で行うことは
- setTextureCoordinate() でテクスチャーに使う画像上の点に番号をつける。
- setTextureCoordinateIndex() で上で付けた点と setCoordinateIndex() で番号づけした点との対応を与える。
が全てである。
テクスチャーに使う画像上の点を与える方法
テクスチャーに使う画像の座標系は (s,t) で表され、画像の大きさにかかわらず、
0 から 1 までの値をとる。
例
下の例では、立方体の各面に画像を張り付けている。TexCoord2f[] t でテクスチャー画像上の点を
8個取り出して、setTextureCoordinateIndices() でそれを立方体の各頂点に対応させている。
立方体に張り付けるときに、大きさを自動的に変換していることに注意する。
import javax.media.j3d.*;
import javax.vecmath.*;
class DiceGeometry extends IndexedQuadArray{
double[] coord;
DiceGeometry(){
super(8,
IndexedLineArray.COORDINATES|
IndexedLineArray.TEXTURE_COORDINATE_2,
24);
coord = new double[3*8];
createGeometry();
setCoordinates(0,coord);
createTexture();
}
public void createGeometry(){
// 0
coord[0] = 0.5;
coord[1] = 0.5;
coord[2] = 0.5;
// 1
coord[3] = -0.5;
coord[4] = 0.5;
coord[5] = 0.5;
// 2
coord[6] = -0.5;
coord[7] = -0.5;
coord[8] = 0.5;
// 3
coord[9] = 0.5;
coord[10] = -0.5;
coord[11] = 0.5;
// 4
coord[12] = 0.5;
coord[13] = 0.5;
coord[14] = -0.5;
// 5
coord[15] = -0.5;
coord[16] = 0.5;
coord[17] = -0.5;
// 6
coord[18] = -0.5;
coord[19] = -0.5;
coord[20] = -0.5;
// 7
coord[21] = 0.5;
coord[22] = -0.5;
coord[23] = -0.5;
// index の設定
setCoordinateIndex(0,0);
setCoordinateIndex(1,1);
setCoordinateIndex(2,2);
setCoordinateIndex(3,3);
setCoordinateIndex(4,0);
setCoordinateIndex(5,4);
setCoordinateIndex(6,5);
setCoordinateIndex(7,1);
setCoordinateIndex(8,0);
setCoordinateIndex(9,3);
setCoordinateIndex(10,7);
setCoordinateIndex(11,4);
setCoordinateIndex(12,1);
setCoordinateIndex(13,5);
setCoordinateIndex(14,6);
setCoordinateIndex(15,2);
setCoordinateIndex(16,5);
setCoordinateIndex(17,4);
setCoordinateIndex(18,7);
setCoordinateIndex(19,6);
setCoordinateIndex(20,2);
setCoordinateIndex(21,6);
setCoordinateIndex(22,7);
setCoordinateIndex(23,3);
}
public void createTexture(){
TexCoord2f[] t = new TexCoord2f[8];
t[0] = new TexCoord2f(0.5f, 0.5f);
t[1] = new TexCoord2f(0f, 0.5f);
t[2] = new TexCoord2f(0f, 0f);
t[3] = new TexCoord2f(0.5f, 0f);
t[4] = new TexCoord2f(1.0f, 0.5f);
t[5] = new TexCoord2f(0.5f, 0.5f);
t[6] = new TexCoord2f(0.5f, 0f);
t[7] = new TexCoord2f(1.0f, 0f);
setTextureCoordinates(0, 0, t);
int ind[] = new int[]{0,3,2,1,
7,6,5,4,
1,2,3,0,
5,4,7,6,
2,3,0,1,
2,7,4,1};
setTextureCoordinateIndices(0, 0, ind);
}
}
posted at 16:55 |
category: /Java/Java3DTips |
固定リンク(テクスチャーの貼り付け方)
Picking した物体の座標を得るには
com.sun.j3d.utils.picking.behaviors.PickingCallback
インターフェースを使うと良い。
PickRotateBehavior や PickTranslateBehavior には
setupCallback() メソッドがあるので、それを使って、
結びつける。すると、Picking した TransformGroup の値が変化したときには
setupCallback(PickingCallback callback) によって結びつけられた callback
の transformChanged() メソッドが実行される。
ActionListner と使い方は似ているので、比較してみると分かりやすいかもしれない。
| インターフェース名 |
ActionListener |
PickingCallback |
| 点火元と結びつけるメソッド |
addActionLisnter() |
setupCallback() |
| 動作を定義するメソッド |
actionPerformed() |
transformChanged() |
PickCallback を継承したクラスを作成し、その中の transformChanged()
メソッドの中に実際の処理を書く。そして、PickTranslateBehavior などの
インスタンスに setupCallback() メソッドでこのクラスのインスタンスを
結びつければ良い。
例えば Picking された TransformGroup の座標を調べるには
PickTranslateBehavior pickTrans
= new PickTranslateBehavior(scene,this,bounds);
pickTrans.setupCallback(new PickingCallback(){
Vector3d vect = new Vector3d(); // 座標を格納する
Transform3D t3d = new Transform3D();
public void transformChanged(int type,TransformGroup tg){
if(type == PickingCallback.TRANSLATE){
tg.getTransform(t3d);
t3d.get(vect);
// ここで座標の処理をする。
}
}
});
のようになる。単に座標を表示するだけのプログラムをサンプルとして下に挙げる。
posted at 16:54 |
category: /Java/Java3DTips |
固定リンク(Picking した物体の座標を得るには)
物体を選択してマウスで動かしたりするには、PickMouseBehavior のサブクラス
を使う。
- 回転させる PickRotateBehavior
- 動かす PickTranslateBehavior
- ズームする PickZoomBehavior
これらは Java3D 1.2 以前では com.sun.j3d.utils.behaviors.picking パッケー
ジに入っていたが、1.2 からは com.sun.j3d.utils.picking.behaviors パッ
ケージに入っている。ここでは新しい方
(com.sun.j3d.utils.picking.behaviors)を使おう。
以下では物体を選んで回転させる方法について解説する。
PickRotateBehavior の部分を他のクラスに変えれば、他も同様にできる。
やるべきことは簡単で、PickRotateBehavior のインスタンスを作成し、
BranchGroup に addChild すればよい。Picking を有効にしたい Node を
あらかじめ setCapability(TransformGroup.ENABLE_PICK_REPORTING) によって
属性を変えておくことを忘れずに。
// ..... 途中から
Canvas3D canvas = new Canvas3D(SimpleUniverse.getPreferredConfiguration());
BranchGroup scene = new BranchGroup();
TransformGroup[] trBox = new TransformGroup[2];
for(int i=0;i<trBox.length;i++){
trBox[i] = new TransformGroup();
trBox[i].setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
trBox[i].setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
trBox[i].setCapability(TransformGroup.ENABLE_PICK_REPORTING);
trBox[i].addChild(new ColorCube(0.1f));
}
BoundingSphere bounds = new BoundingSphere(new Point3d(), 100.0);
// 立方体を別々に動かすためのピッキング
pickRot = new PickRotateBehavior(scene,canvas,bounds);
scene.addChild(pickRot);
// ..... 以下略
上の例では、trBox[0]、trBox[1] が Picking 可能になっている。
そこに立方体が addChild されているので、最後の2行によって、
立方体を Picking によって個別に回転することができる。
posted at 16:19 |
category: /Java/Java3DTips |
固定リンク(Picking するには)
物体を選択したときの動作を自分で定義するには、
com.sun.j3d.utils.picking.behaviors.PickMouseBehavior を自分で拡張する。
PickMouseBehavior は抽象クラスで、updateScene(int xpos,int ypos) メソッ
ドが抽象メソッドであるから、これを拡張する。ここでは Picking した物体
の座標を表示するプログラムを取り上げてみる。
PickMouseBehavior には pickCanvas と言うフィールドがある。
このクラスの setShapeLocation(xpos,ypos) と
pickClosest() メソッドを使ってマウスをクリックした点に一番近い物は何かを
を調べることができる。pickClosest() は調査の結果を PickResult クラスで返
すので、実際に Node を取り出すには、PickResult クラスの getNode() メソッ
ドを用いる。雛型は次の通り。
public void updateScene(int xpos,int ypos){
TransformGroup tg = null;
// マウスの座標を与える
pickCanvas.setShapeLocation(xpos,ypos);
// マウスの位置に一番近いノード
PickResult res = pickCanvas.pickClosest();
if(res != null){
// 何かが Picking されているときには
// その TransformGroup を取り出す
if(((tg = (TransformGroup)res.getNode(PickResult.TRANSFORM_GROUP)) != null) &&
(tg.getCapability(TransformGroup.ALLOW_TRANSFORM_READ)) &&
(tg.getCapability(TransformGroup.ALLOW_TRANSFORM_WRITE))){
// ここで tg についての処理を行う
}
}
}
今の場合は、tg についての処理のところで、getTransform() で座標を取り出し、
それを TextField に書き込んでいる。
posted at 16:16 |
category: /Java/Java3DTips |
固定リンク(Picking をカスタマイズするには)
Java3D 付属の Picking ではマウスで物体を選択することはできるが、
それを動かすときに、マウスカーソルに追従させることはできない。
それを可能にするための Behavior を書き直してみた。
もちろんx座標とy座標方向にしか動かすことはできない。
z座標方向は画面と垂直なので、マウスカーソルの位置で奥行を
変化させることはできないため。
また、Picking した物体がぶら下がっている TransformGroup を
動かしたときに標準の Picking では違った方向に動く仕様になっているが、
それも大域座標で計算しなおすような仕様に変更してある。
以下にソースを挙げる。使いかたは、PickingTranslateBehavior とほぼ同じ。
ただ、コンストラクタの引数の順番が異なる。
/**
* @author TOKUNAGA Ken-ichi
* @version Time-stamp: <03/02/28 03:17:20 tkenichi>
*
* 物体をピッキングして、それをマウスカーソルに追従させる。
*/
import com.sun.j3d.utils.picking.*;
import com.sun.j3d.utils.picking.behaviors.*;
import com.sun.j3d.utils.behaviors.mouse.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.media.j3d.*;
import javax.vecmath.*;
public class MouseDragBehavior extends Behavior
implements MouseBehaviorCallback{
Vector3d currV3d = new Vector3d(),diffV3d = new Vector3d();
Point3d cursorP3d = new Point3d(),viewP3d = new Point3d();
Vector4d currV4d = new Vector4d();
private PickingCallback callback = null;
private PickCanvas pickCanvas;
private WakeupOr wakeupCondition;
private boolean picked = false;
// ピッキングした座標を格納する
private TransformGroup currGrp;
private Transform3D
currT3d = new Transform3D(), // ピッキングした座標
localT3d = new Transform3D(), // 局所座標
inverse = new Transform3D(), // 逆変換
plate2world = new Transform3D(); // imageplate から仮想空間への変換
public MouseDragBehavior(Canvas3D canvas,
BranchGroup root,
Bounds bounds){
currGrp = new TransformGroup();
currGrp.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
currGrp.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
currGrp.setCapability(TransformGroup.ALLOW_LOCAL_TO_VWORLD_READ);
// Behavior は Leaf だからその上の BranchGroup につける
root.addChild(currGrp);
pickCanvas = new PickCanvas(canvas, root);
pickCanvas.setMode(PickCanvas.BOUNDS);
setSchedulingBounds(bounds);
}
public void setMode(int pickMode) {
pickCanvas.setMode(pickMode);
}
public int getMode() {
return pickCanvas.getMode();
}
public void setTolerance(float tolerance) {
pickCanvas.setTolerance(tolerance);
}
public float getTolerance() {
return pickCanvas.getTolerance();
}
public void initialize() {
// マウスを押したとき、離したとき、ドラッグしたときに
// 起動する
WakeupCriterion[] conditions = new WakeupCriterion[3];
conditions[0] = new WakeupOnAWTEvent(MouseEvent.MOUSE_DRAGGED);
conditions[1] = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);
conditions[2] = new WakeupOnAWTEvent(MouseEvent.MOUSE_RELEASED);
wakeupCondition = new WakeupOr(conditions);
wakeupOn(wakeupCondition);
}
public void processStimulus (Enumeration criteria) {
WakeupCriterion wakeup;
AWTEvent[] evt = null;
int xpos = 0, ypos = 0;
MouseEvent mevent;
// AWTEvent を取り出す
while(criteria.hasMoreElements()) {
wakeup = (WakeupCriterion)criteria.nextElement();
if (wakeup instanceof WakeupOnAWTEvent)
evt = ((WakeupOnAWTEvent)wakeup).getAWTEvent();
}
// MouseEvent の場合
if (evt[0] instanceof MouseEvent){
mevent = (MouseEvent) evt[0];
// マウスカーソルの2次元ピクセル座標
xpos = mevent.getPoint().x;
ypos = mevent.getPoint().y;
switch(mevent.getID()){
case MouseEvent.MOUSE_PRESSED:
// 押されたとき
System.out.println("mouse pressed");
// ピッキングする
if (!mevent.isAltDown() && mevent.isMetaDown()){
pickingTransformGroup(xpos,ypos);
}
break;
case MouseEvent.MOUSE_RELEASED:
// 離したとき
// ピッキングをやめる
picked = false;
break;
case MouseEvent.MOUSE_DRAGGED:
// ドラッグしたとき
// ピッキングされた TransformGroup を動かす
if (picked && !mevent.isAltDown() && mevent.isMetaDown()){
translate(xpos,ypos);
}
break;
default:
System.out.println("other event");
}
}
wakeupOn (wakeupCondition);
}
// ピッキングして TransformGroup を currGrp に格納する
private void pickingTransformGroup(int xpos,int ypos){
TransformGroup tg = null;
// PickCanvas をつかってピッキングしているノードを探す
pickCanvas.setShapeLocation(xpos, ypos);
// LineArray と Vertex の両方が重なっている場合も
// Vertex を取り出すために、pickAll() で全て取り出す
PickResult pr[] = pickCanvas.pickAll();
if(pr != null){
for(int i=0;i<pr.length;i++){
System.out.println(i + " = " + pr[i].getNode(PickResult.GROUP));
// ピッキングした結果が読み書き可能な TransformGroup を取り出す
if ((pr[i] != null) &&
((tg = (TransformGroup)pr[i].getNode(PickResult.TRANSFORM_GROUP))
!= null) &&
(tg.getCapability(TransformGroup.ALLOW_TRANSFORM_READ)) &&
(tg.getCapability(TransformGroup.ALLOW_TRANSFORM_WRITE))){
System.out.println("success to pick " + tg);
picked = true;
// ピッキングした結果の参照を currGrp にセットする
currGrp = tg;
// 物体が乗っている座標系とその逆変換を得る
currGrp.getLocalToVworld(localT3d);
System.out.println(localT3d);
inverse.invert(localT3d);
System.out.println(inverse);
// ImagePlate から仮想空間への変換行列を得る
pickCanvas.getCanvas().
getImagePlateToVworld(plate2world);
// 視点の位置を得る
pickCanvas.getCanvas().getCenterEyeInImagePlate(viewP3d);
plate2world.transform(viewP3d);
// マウスカーソルの位置に物体を合わせる
translate(xpos,ypos);
// freePickResult(pr);
}
}
}
}
private void translate(int xpos,int ypos){
// マウスカーソルのimageplate上の位置を得る
pickCanvas.getCanvas().
getPixelLocationInImagePlate(xpos,ypos,cursorP3d);
plate2world.transform(cursorP3d);
// ピッキングした物体の座標を通る
currGrp.getTransform(currT3d);
currT3d.get(currV3d);
currV4d.set(currV3d.x,
currV3d.y,
currV3d.z,
1);
localT3d.transform(currV4d);
currV3d.set(currV4d.x,
currV4d.y,
currV4d.z);
//System.out.println("cursor = " + cursorP3d);
//System.out.println("current = " + currV3d);
// 物体の座標をマウスカーソルの位置にあわせる
// view と垂直方向な平面上を移動する
currV3d.sub(viewP3d);
cursorP3d.sub(viewP3d);
double alpha =
(currV3d.x * viewP3d.x +
currV3d.y * viewP3d.y +
currV3d.z * viewP3d.z) /
(cursorP3d.x * viewP3d.x +
cursorP3d.y * viewP3d.y +
cursorP3d.z * viewP3d.z);
currV3d.scaleAdd(alpha,cursorP3d,viewP3d);
// 大域座標を局所座標に直す
currV4d.set(currV3d.x,
currV3d.y,
currV3d.z,
1);
inverse.transform(currV4d);
currV3d.set(currV4d.x,
currV4d.y,
currV4d.z);
currT3d.setTranslation(currV3d);
currGrp.setTransform(currT3d);
transformChanged(MouseBehaviorCallback.TRANSLATE,
currT3d);
}
public void setupCallback(PickingCallback callback){
this.callback = callback;
}
public void transformChanged(int type,Transform3D transform){
if(callback != null){
callback.transformChanged(type,currGrp);
}
}
}
posted at 16:14 |
category: /Java/Java3DTips |
固定リンク(Picking した物体を Drag するには)
Sun, 12 Feb 2006
Behavior を Thread のように使ってアニメーションをするには
class TimerBehavior extends Behavior{
WakeupOnElapsedTime won;
public TimerBehavior(long sleep){
won = new WakeupOnElapsedTime(sleep);
}
public void initialize(){
wakeupOn(won);
}
// Thread.run() に相当
public void processStimulus(java.util.Enumeration criteria){
// ここで 3D の更新を行う
wakeupOn(won);
}
}
のような Behavior を継承したクラスを作成して addChild すればよい。
実行すると、sleep ミリ秒毎に 3D の更新を行ってアニメーションのように見える。
ただしこのままだと、動きっぱなしで制御することができない。
ボタンによって Behavior を On/Off するには postId() メソッドを用いて、
次のようにする。
- Behavior の WakeupOnCriterion を WakeupOnElapsedTime と
WakeupOnBehaviorPost の2種類用意する。
- さらに現在どちらの WakeupOnCriterion を使っているかを判別するための
flag を準備する。ここでは true のとき WakeupOnElapsedTime を呼ぶこと
にする。
- initialize() の中で、
wakeupOn() に WakeupOnBehaviorPost クラスのインスタンスをセットし、
flag を false にする。
- processStimulus() メソッドで flag の値にしたがって、
次に wakeupOn() する WakeupOnCriterion を変更する。
Behavior の wakeupOn() は外から呼ぶことはできないが、postId()
は外から呼ぶことができる。Behavior を On するには、
- flag を true にする。
- この Behavior クラスのインスタンスの postId() メソッドを呼ぶ。
とすればよく、Off するには、
だけでよい。
具体例で言うと、Behavior のほうは
class TimerBehavior extends Behavior{
private WakeupCriterion awtWakeon,timeWakeon;
boolean flag; // true のときは timeWakeon を待つ
public TimerBehavior(long time){
timeWakeon = new WakeupOnElapsedTime(time);
awtWakeon = new WakeupOnBehaviorPost(this,
MouseEvent.MOUSE_CLICKED);
}
public void initialize() {
wakeupOn(awtWakeon);
flag = false;
}
public void processStimulus(java.util.Enumeration criteria) {
if(flag){
// ここでアニメーションの処理をする
wakeupOn(timeWakeon);
}else{
wakeupOn(awtWakeon);
}
}
}
のようにする。
TimerBehavior のインスタンスを behavior とするとき、
呼び出すメソッドは VirtualUniverse や Applet のなかに
public void stopBehavior(){
behavior.flag = false;
}
public void startBehavior(){
behavior.flag = true;
behavior.postId(MouseEvent.MOUSE_CLICKED);
}
のように記述する。このメソッドをボタンを押したときの
actionPerformed() で呼べばよい。
posted at 22:16 |
category: /Java/Java3DTips |
固定リンク(Java3D Behavior をボタンで On/Off するには)
Material の属性の意味。
- ambient 周囲の色
- diffuse 反射の色
- emissive 放射する色
- specular 鏡の色
posted at 22:14 |
category: /Java/Java3DTips |
固定リンク(Java3D Material)
Transform3D ではその状態を、4行4列の行列、3行3列の行列、3次元のベクトル、
4元数で設定することができます。それぞれの間の関係をまず調べてみます。
4行4列の行列が一番情報量が多いので、おそらく4行4列の行列で表現すれば
いいはず。set() メソッドがその他の形式から4行4列の行列への写像を与えてい
ると考える。
- スカラーから set(double scale)
s -> [s 0 0 0]
[0 s 0 0]
[0 0 s 0]
[0 0 0 1]
- 3次元ベクトルから set(Vector3f trans)
[x0] -> [1 0 0 x0]
[x1] [0 1 0 x1]
[x2] [0 0 1 x2]
[0 0 0 1 ]
- スカラーと3次元ベクトルから set(double scale, Vector3d v1)
s,[x0] -> [s 0 0 x0]
[x1] [0 s 0 x1]
[x2] [0 0 s x2]
[0 0 0 1 ]
- 3次元ベクトルとスカラーから set(Vector3d v1,double scale)
[x0],s -> [s 0 0 s*x0]
[x1] [0 s 0 s*x1]
[x2] [0 0 s s*x2]
[0 0 0 1 ]
- 3行3列の行列から
[m00 m01 m02] -> [m00 m01 m02 0]
[m10 m11 m12] [m10 m11 m12 0]
[m20 m21 m22] [m20 m21 m22 0]
[0 0 0 1]
4元数は Quat4d では、フィールド x、y、z、w で表しているが、これは
数学の書き方をすると、
(x,y,z,w) = w + xi + yj + zk = q
である。4元数 q の随伴表現で、4元数の虚成分
(Sp(1)のリー環)に作用したものと考える。すなわち
4元数 q が3次元ベクトル v = (v1,v2,v3) に対して
q(v) = q v q-1
で作用する。この作用は q に対して一意ではないことに注意する。
これが set(Quat4d q) に他ならない。
[x] -> [(x*x-y*y-z*z+w*w)/r 2*(x*y-w*z)/r 2*(x*z+y*w)/r 0]
[y] [2*(x*y+w*z)/r (-x*x+y*y-z*z+w*w)/r 2*(y*z-x*w)/r 0]
[z] [2*(x*z-y*w)/r 2*(y*z+x*w)/r (-x*x-y*y+z*z+w*w)/r 0]
[w] [0 0 0 1]
ただし r = x*x + y*y + z*z + w*w となる。
posted at 22:12 |
category: /Java/Java3DTips |
固定リンク(Java3D Transform3D の set メソッドの具体的仕様)
Behavior とは抽象クラスで、initialize() と processStimulus() メソッド
を実装して用いる。これも Java3D の Node を継承しているので、
木構造に addChild() して使う。
initialize() は初期化をする。
processStimulus() は wakeupOn(WakeupCriterion criteria) によって
設定された起動条件 criteria が満たされたときに呼ばれるメソッド。
普通 initialize() のなかで wakeupOn() を呼び出し、最初の起動条件を
設定する。接待条件を満たすたびに processStimulus() を実行したいときは
processStimulus() の中で再度 wakeupOn() を呼び出す。
criteria で記述された条件や processStimulus() で記述された動作によって
アニメーションやマウスやキーボードなどのユーザインターフェースを
実現することができる。
実は PickMouseBehavior や Interpolator も Behavior の継承クラス。
Behavior で Animation をするには
WakeupCriterion に指定時間の経過後に起動する WakeupOnElapsedTime
もしくは Frame が描き終わったときに起動する WakeupOnElapsedFrame
を使うとよい。
posted at 22:09 |
category: /Java/Java3DTips |
固定リンク(Java3D Behaviorについて)