|
/**********************************************************
Copyright (C) 2001 Daniel Selman
First distributed with the book "Java 3D Programming"
by Daniel Selman and published by Manning Publications.
http://manning.com/selman
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, version 2.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
The license can be found on the WWW at:
http://www.fsf.org/copyleft/gpl.html
Or by writing to:
Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Authors can be contacted at:
Daniel Selman: daniel@selman.org
If you make changes you think others would like, please
contact one of the authors or someone at the
www.j3d.org web site.
**************************************************************/
import java.applet.Applet;
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.GraphicsConfigTemplate;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Label;
import java.awt.Panel;
import java.awt.TextField;
import java.awt.event.MouseEvent;
import java.io.File;
import java.net.URL;
import java.util.Enumeration;
import javax.media.j3d.Appearance;
import javax.media.j3d.AudioDevice;
import javax.media.j3d.Background;
import javax.media.j3d.Behavior;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.Bounds;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.GraphicsConfigTemplate3D;
import javax.media.j3d.Group;
import javax.media.j3d.Locale;
import javax.media.j3d.PhysicalBody;
import javax.media.j3d.PhysicalEnvironment;
import javax.media.j3d.PolygonAttributes;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.View;
import javax.media.j3d.ViewPlatform;
import javax.media.j3d.VirtualUniverse;
import javax.media.j3d.WakeupCriterion;
import javax.media.j3d.WakeupOnAWTEvent;
import javax.media.j3d.WakeupOr;
import javax.vecmath.Color3f;
import javax.vecmath.Matrix3d;
import javax.vecmath.Matrix4d;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;
import com.sun.j3d.audioengines.javasound.JavaSoundMixer;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.ColorCube;
/**
* This example illustrates the mouse behaviors defined in the
* org.selman.java3d.book package. These improved mouse behaviors: - handle
* TransformGroups above the target TG properly <br>- can be applied to any
* object, not just TGs <br>- support interface reporting to give easy feedback
* on all manipulations <br>- better motion speed control <br>- range
* validation to clamp scales and translation between limits <br>
*/
public class MouseNavigateTest extends Java3dApplet implements
ScaleChangeListener, RotationChangeListener, TranslationChangeListener {
private static int m_kWidth = 300;
private static int m_kHeight = 400;
// create some UI to provide feedback on all mouse
// manipulations
Label m_RotationLabel = null;
TextField m_RotationFieldX = null;
TextField m_RotationFieldY = null;
TextField m_RotationFieldZ = null;
Label m_TranslationLabel = null;
TextField m_TranslationFieldX = null;
TextField m_TranslationFieldY = null;
TextField m_TranslationFieldZ = null;
Label m_ScaleLabel = null;
TextField m_ScaleFieldZ = null;
TextField m_ScaleFieldY = null;
TextField m_ScaleFieldX = null;
public MouseNavigateTest() {
initJava3d();
}
protected void addCanvas3D(Canvas3D c3d) {
setLayout(new BorderLayout());
add(c3d, BorderLayout.CENTER);
Panel controlPanel = new Panel();
// add the UI to the frame
m_RotationLabel = new Label("Rotation: ");
m_RotationFieldX = new TextField("0.00");
m_RotationFieldY = new TextField("0.00");
m_RotationFieldZ = new TextField("0.00");
controlPanel.add(m_RotationLabel);
controlPanel.add(m_RotationFieldX);
controlPanel.add(m_RotationFieldY);
controlPanel.add(m_RotationFieldZ);
m_TranslationLabel = new Label("Translation: ");
m_TranslationFieldX = new TextField("0.00");
m_TranslationFieldY = new TextField("0.00");
m_TranslationFieldZ = new TextField("0.00");
controlPanel.add(m_TranslationLabel);
controlPanel.add(m_TranslationFieldX);
controlPanel.add(m_TranslationFieldY);
controlPanel.add(m_TranslationFieldZ);
m_ScaleLabel = new Label("Scale: ");
m_ScaleFieldX = new TextField("0.00");
m_ScaleFieldY = new TextField("0.00");
m_ScaleFieldZ = new TextField("0.00");
controlPanel.add(m_ScaleLabel);
controlPanel.add(m_ScaleFieldX);
controlPanel.add(m_ScaleFieldY);
controlPanel.add(m_ScaleFieldZ);
add(controlPanel, BorderLayout.SOUTH);
doLayout();
}
protected double getScale() {
return 1.0;
}
// do nothing for these notifications from the mouse behaviors
public void onStartDrag(Object target) {
}
public void onEndDrag(Object target) {
}
public void onApplyTransform(Object target) {
}
public void onAdjustTransform(Object target, int xpos, int ypos) {
}
// called by TornadoMouseRotate
// yes, those really are Euler angles for the objects rotation
public void onRotate(Object target, Point3d point3d) {
m_RotationFieldX.setText(String.valueOf((int) java.lang.Math
.toDegrees(point3d.x)));
m_RotationFieldY.setText(String.valueOf((int) java.lang.Math
.toDegrees(point3d.y)));
m_RotationFieldZ.setText(String.valueOf((int) java.lang.Math
.toDegrees(point3d.z)));
}
// called by TornadoMouseScale
public void onScale(Object target, Vector3d scale) {
m_ScaleFieldX.setText(String.valueOf(scale.x));
m_ScaleFieldY.setText(String.valueOf(scale.y));
m_ScaleFieldZ.setText(String.valueOf(scale.z));
}
// called by TornadoMouseTranslate
public void onTranslate(Object target, Vector3d vTranslation) {
m_TranslationFieldX.setText(String.valueOf(vTranslation.x));
m_TranslationFieldY.setText(String.valueOf(vTranslation.y));
m_TranslationFieldZ.setText(String.valueOf(vTranslation.z));
}
// we want a black background
protected Background createBackground() {
return null;
}
protected BranchGroup createSceneBranchGroup() {
BranchGroup objRoot = super.createSceneBranchGroup();
// note that we are creating a TG *above* the TG
// the is being controlled by the mouse behaviors.
// The SUN mouse translate behavior would fail in this
// instance as all movement would be in the X-Y plane
// irrespective of any TG above the object.
// The TornadoMouseTranslate behavior always moves an object
// parrallel to the image plane
TransformGroup objTrans1 = new TransformGroup();
Transform3D t3d = new Transform3D();
objTrans1.getTransform(t3d);
t3d.setEuler(new Vector3d(0.9, 0.8, 0.3));
objTrans1.setTransform(t3d);
TransformGroup objTrans = new TransformGroup();
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
// create the mouse scale behavior and set limits
TornadoMouseScale mouseScale = new TornadoMouseScale(5, 0.1f);
mouseScale.setMinScale(new Point3d(0.5, 0.5, 0.5));
mouseScale.setMaxScale(new Point3d(2, 2, 2));
mouseScale.setObject(objTrans);
mouseScale.setChangeListener(this);
mouseScale.setSchedulingBounds(getApplicationBounds());
objTrans.addChild(mouseScale);
// create the mouse rotate behavior
TornadoMouseRotate mouseRotate = new TornadoMouseRotate(0.001, 0.001);
mouseRotate.setInvert(true);
mouseRotate.setObject(objTrans);
mouseRotate.setChangeListener(this);
mouseRotate.setSchedulingBounds(getApplicationBounds());
objTrans.addChild(mouseRotate);
// create the mouse translate behavior and set limits
TornadoMouseTranslate mouseTrans = new TornadoMouseTranslate(0.005f);
mouseTrans.setObject(objTrans);
mouseTrans.setChangeListener(this);
mouseTrans.setMinTranslate(new Point3d(-4, -4, -4));
mouseTrans.setMaxTranslate(new Point3d(4, 4, 4));
mouseTrans.setSchedulingBounds(getApplicationBounds());
objTrans.addChild(mouseTrans);
objTrans.addChild(new ColorCube(0.5));
// create some axis for the world to show it has been rotated
ColorCube axis = new ColorCube(5.0);
Appearance app = new Appearance();
app
.setPolygonAttributes(new PolygonAttributes(
PolygonAttributes.POLYGON_LINE,
PolygonAttributes.CULL_NONE, 0));
axis.setAppearance(app);
objTrans1.addChild(axis);
objTrans1.addChild(objTrans);
objRoot.addChild(objTrans1);
return objRoot;
}
public static void main(String[] args) {
MouseNavigateTest mouseTest = new MouseNavigateTest();
mouseTest.saveCommandLineArguments(args);
new MainFrame(mouseTest, m_kWidth, m_kHeight);
}
}
//*****************************************************************************
/**
* TornadoMouseRotate
*
* Custom mouse rotation behaviour
*
* @author Daniel Selman
* @version 1.0
*/
//*****************************************************************************
class TornadoMouseRotate extends TornadoMouseBehavior {
protected double m_FactorX = 0.001;
protected double m_FactorY = 0.001;
protected Transform3D m_TransformX = null;
protected Transform3D m_TransformY = null;
protected boolean m_bInvert = false;
//*****************************************************************************
/**
* @param xf
* the x rotation scale factor
* @param yf
* the y rotation scale factor
*/
//*****************************************************************************
public TornadoMouseRotate(double xf, double yf) {
m_FactorX = xf;
m_FactorY = yf;
m_TransformX = new Transform3D();
m_TransformY = new Transform3D();
m_bInvert = false;
}
protected boolean isStartBehaviorEvent(java.awt.event.MouseEvent evt) {
int nId = evt.getID();
return ((nId == MouseEvent.MOUSE_DRAGGED) && (evt.isAltDown() == false) && (evt
.isMetaDown() == false));
}
//*****************************************************************************
/**
* @param bInvert
* true to invert the Y axis
*/
//*****************************************************************************
public void setInvert(boolean bInvert) {
m_bInvert = bInvert;
}
// this behavior is relative to the *screen*
// the current rotation of the object etc. is ignored
protected boolean isRelativeToObjectCoordinates() {
return false;
}
protected void applyVectorToObject(Vector3f vector) {
TransformGroup tg = getTransformGroup();
if (tg != null) {
tg.getTransform(m_Transform3D);
double x_angle = vector.y * m_FactorX;
double y_angle = vector.x * m_FactorY;
m_TransformX.rotX(x_angle);
m_TransformY.rotY(y_angle);
Matrix4d mat = new Matrix4d();
// Remember old matrix
m_Transform3D.get(mat);
// Translate to origin
m_Transform3D.setTranslation(new Vector3d(0.0, 0.0, 0.0));
if (m_bInvert != false) {
m_Transform3D.mul(m_Transform3D, m_TransformX);
m_Transform3D.mul(m_Transform3D, m_TransformY);
} else {
m_Transform3D.mul(m_TransformX, m_Transform3D);
m_Transform3D.mul(m_TransformY, m_Transform3D);
}
// Set old translation back
Vector3d translation = new Vector3d(mat.m03, mat.m13, mat.m23);
m_Transform3D.setTranslation(translation);
// save the new Transform3D
applyTransform();
if (m_Listener != null) {
Point3d rotate = Euler.getEulerRotation(m_Transform3D);
((RotationChangeListener) m_Listener)
.onRotate(m_Object, rotate);
}
}
}
} // TornadoMouseRotate
class Euler {
public static int EulOrdXYZs() {
return EulOrd(X, EulParEven, EulRepNo, EulFrmS);
}
public static int EulOrdXYXs() {
return EulOrd(X, EulParEven, EulRepYes, EulFrmS);
}
public static int EulOrdXZYs() {
return EulOrd(X, EulParOdd, EulRepNo, EulFrmS);
}
public static int EulOrdXZXs() {
return EulOrd(X, EulParOdd, EulRepYes, EulFrmS);
}
public static int EulOrdYZXs() {
return EulOrd(Y, EulParEven, EulRepNo, EulFrmS);
}
public static int EulOrdYZYs() {
return EulOrd(Y, EulParEven, EulRepYes, EulFrmS);
}
public static int EulOrdYXZs() {
return EulOrd(Y, EulParOdd, EulRepNo, EulFrmS);
}
public static int EulOrdYXYs() {
return EulOrd(Y, EulParOdd, EulRepYes, EulFrmS);
}
public static int EulOrdZXYs() {
return EulOrd(Z, EulParEven, EulRepNo, EulFrmS);
}
public static int EulOrdZXZs() {
return EulOrd(Z, EulParEven, EulRepYes, EulFrmS);
}
public static int EulOrdZYXs() {
return EulOrd(Z, EulParOdd, EulRepNo, EulFrmS);
}
public static int EulOrdZYZs() {
return EulOrd(Z, EulParOdd, EulRepYes, EulFrmS);
}
/* Rotating axes */
public static int EulOrdZYXr() {
return EulOrd(X, EulParEven, EulRepNo, EulFrmR);
}
public static int EulOrdXYXr() {
return EulOrd(X, EulParEven, EulRepYes, EulFrmR);
}
public static int EulOrdYZXr() {
return EulOrd(X, EulParOdd, EulRepNo, EulFrmR);
}
public static int EulOrdXZXr() {
return EulOrd(X, EulParOdd, EulRepYes, EulFrmR);
}
public static int EulOrdXZYr() {
return EulOrd(Y, EulParEven, EulRepNo, EulFrmR);
}
public static int EulOrdYZYr() {
return EulOrd(Y, EulParEven, EulRepYes, EulFrmR);
}
public static int EulOrdZXYr() {
return EulOrd(Y, EulParOdd, EulRepNo, EulFrmR);
}
public static int EulOrdYXYr() {
return EulOrd(Y, EulParOdd, EulRepYes, EulFrmR);
}
public static int EulOrdYXZr() {
return EulOrd(Z, EulParEven, EulRepNo, EulFrmR);
}
public static int EulOrdZXZr() {
return EulOrd(Z, EulParEven, EulRepYes, EulFrmR);
}
public static int EulOrdXYZr() {
return EulOrd(Z, EulParOdd, EulRepNo, EulFrmR);
}
public static int EulOrdZYZr() {
return EulOrd(Z, EulParOdd, EulRepYes, EulFrmR);
}
public static int EulFrm(int ord) {
// DCS, was unsigned
return ((ord) & 1);
}
public static int EulRep(int ord) {
// DCS, was unsigned
return (((ord) >> 1) & 1);
}
public static int EulPar(int ord) {
return (((ord) >> 2) & 1);
}
public static int EulAxI(int ord) {
// DCS, was unsigned
return ((int) (EulSafe((((ord) >> 3) & 3))));
}
public static int EulAxJ(int ord) {
int i = 0;
if (EulPar(ord) == EulParOdd)
i = 1;
return ((int) (EulNext(EulAxI(ord) + i)));
}
public static int EulAxK(int ord) {
int i = 0;
if (EulPar(ord) != EulParOdd)
i = 1;
return ((int) (EulNext(EulAxI(ord) + i)));
}
public static int EulAxH(int ord) {
if (EulRep(ord) == EulRepNo)
return EulAxK(ord);
return EulAxI(ord);
}
public static int EulOrd(int i, int p, int r, int f) {
return (((((((i) << 1) + (p)) << 1) + (r)) << 1) + (f));
}
// enum
static final int X = 0;
static final int Y = 1;
static final int Z = 2;
static final int W = 3;
static final int EulRepNo = 0;
static final int EulRepYes = 1;
static final int EulParEven = 0;
static final int EulParOdd = 1;
static final int EulFrmS = 0;
static final int EulFrmR = 1;
static final float FLT_EPSILON = 1.192092896e-07F;
static EulGetOrdInfo EulGetOrd(int ord) {
EulGetOrdInfo info = new EulGetOrdInfo();
// note, used to be unsigned!
int o = ord;
info.f = o & 1;
o >>= 1;
info.s = o & 1;
o >>= 1;
info.n = o & 1;
o >>= 1;
info.i = EulSafe(o & 3);
info.j = EulNext(info.i + info.n);
info.k = EulNext(info.i + 1 - info.n);
if (info.s != 0)
info.h = info.k;
else
info.h = info.i;
return info;
}
static int EulSafe(int val) {
int[] valArray = { 0, 1, 2, 0 };
return valArray[val];
}
static int EulNext(int val) {
int[] valArray = { 1, 2, 0, 1 };
return valArray[val];
}
// float HMatrix[4][4];
/* Convert matrix to Euler angles (in radians). */
public static EulerAngles Eul_FromMatrix(float[][] M, int order) {
EulerAngles ea = new EulerAngles();
EulGetOrdInfo info = EulGetOrd(order);
int i = info.i;
int j = info.j;
int k = info.k;
int h = info.h;
int n = info.n;
int s = info.s;
int f = info.f;
if (s == EulRepYes) {
double sy = Math.sqrt(M[i][j] * M[i][j] + M[i][k] * M[i][k]);
if (sy > 16 * FLT_EPSILON) {
ea.x = (float) Math.atan2(M[i][j], M[i][k]);
ea.y = (float) Math.atan2(sy, M[i][i]);
ea.z = (float) Math.atan2(M[j][i], -M[k][i]);
} else {
ea.x = (float) Math.atan2(-M[j][k], M[j][j]);
ea.y = (float) Math.atan2(sy, M[i][i]);
ea.z = 0;
}
} else {
double cy = Math.sqrt(M[i][i] * M[i][i] + M[j][i] * M[j][i]);
if (cy > 16 * FLT_EPSILON) {
ea.x = (float) Math.atan2(M[k][j], M[k][k]);
ea.y = (float) Math.atan2(-M[k][i], cy);
ea.z = (float) Math.atan2(M[j][i], M[i][i]);
} else {
ea.x = (float) Math.atan2(-M[j][k], M[j][j]);
ea.y = (float) Math.atan2(-M[k][i], cy);
ea.z = 0;
}
}
if (n == EulParOdd) {
ea.x = -ea.x;
ea.y = -ea.y;
ea.z = -ea.z;
}
if (f == EulFrmR) {
float t = ea.x;
ea.x = ea.z;
ea.z = t;
}
ea.w = order;
return (ea);
}
/* Convert quaternion to Euler angles (in radians). */
public static EulerAngles Eul_FromQuat(Quat q, int order) {
float[][] M = new float[4][4];
double Nq = q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w;
double s = (Nq >
|