/* ========================
* JSynoptic : a free Synoptic editor
* ========================
*
* Project Info: http://jsynoptic.sourceforge.net/index.html
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 2001-2004, by :
* Corporate:
* Astrium SAS
* Individual:
* Claude Cazenave
*
* $Id: Frame3DJava3D.java,v 1.13 2006/11/23 15:45:31 ogor Exp $
*
* Changes
* -------
* 9-Dec-2003 : Creation date (CC);
*
*/
package syn3d.ui.java3d;
import java.awt.BorderLayout;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
import javax.media.j3d.AmbientLight;
import javax.media.j3d.Appearance;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.Bounds;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.Node;
import javax.media.j3d.PolygonAttributes;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.View;
import javax.swing.JFrame;
import javax.vecmath.AxisAngle4f;
import javax.vecmath.Color3f;
import javax.vecmath.Matrix4f;
import javax.vecmath.Vector3f;
import syn3d.base.ActiveNode;
import syn3d.base.PluginManager;
import syn3d.nodes.SceneNode;
import syn3d.nodes.java3d.SceneNodeJava3D;
import syn3d.ui.Frame3DBase;
import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.picking.PickCanvas;
import com.sun.j3d.utils.picking.PickResult;
import com.sun.j3d.utils.picking.PickTool;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.universe.Viewer;
import com.sun.j3d.utils.universe.ViewingPlatform;
import syn3d.base.java3d.Java3DPluginManager;
/**
* Class description ...
*
* @author Claude CAZENAVE
*
*/
public class Frame3DJava3D extends Frame3DBase {
protected JFrame frame;
protected View view;
protected Viewer viewer;
protected ViewingPlatform viewingPlatform;
protected Canvas3D canvas;
protected SceneNodeJava3D scene;
protected ArrayList lastPickSelection = new ArrayList();
protected int lastMouseX = -1, lastMouseY = -1;
protected Transform3D transform;
protected Transform3D tempTransform;
public SceneNode getScene() {
return scene;
}
/**
* @return Returns the viewer.
*/
public Viewer getViewer() {
return viewer;
}
/**
* @return the view
*/
public View getView() {
return view;
}
/**
* @param owner
* @param x
* @param y
* @param width
* @param height
*/
public Frame3DJava3D(SceneNodeJava3D scene, String t, int x, int y, int width, int height, PluginManager pm) {
super(scene, t, x, y, width, height,pm);
this.scene = scene;
}
protected void createPeer(Object owner){
SceneNodeJava3D scene = (SceneNodeJava3D)owner;
canvas = scene.getCanvas3D();
frame=new JFrame(SimpleUniverse.getPreferredConfiguration());
frame.getContentPane().setLayout(new BorderLayout());
frame.setLocation(x, y);
frame.setSize(width,height);
frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
frame.getContentPane().add("Center", canvas);
peerComponent=frame;
peerWindow=frame;
transform=new Transform3D();
// avoids cumulative rounding errors
// This should avoid "non-congruent transform above view transform" exception
// Warning: In some cases, this exception still appears. This is a known J3D bug, that
// should hopefully be corrected in J3D v1.4
transform.setAutoNormalize(true);
tempTransform=new Transform3D();
viewingPlatform = scene.getUniverse().getViewingPlatform();
viewer = viewingPlatform.getViewers()[0];
view = viewer.getView();
// Use J3D recommendation to set this between 100 and 1000
// default is a factor of 100 (front=0.1, back=10)
// Make this 1000 to have more space
view.setBackClipDistance(view.getFrontClipDistance() * 1000);
canvas.addMouseListener(this);
canvas.addMouseMotionListener(this);
canvas.addMouseWheelListener(this);
canvas.addKeyListener(this);
frame.show();
}
public void setPerspective(boolean p){
super.setPerspective(p);
if (perspective)
view.setProjectionPolicy(View.PERSPECTIVE_PROJECTION);
else
view.setProjectionPolicy(View.PARALLEL_PROJECTION);
}
public void rotate2D(int newX, int newY, boolean inverse) {
int lightRotationMode;
if (scene!=null) lightRotationMode = scene.getLightRotationMode();
else lightRotationMode = -1;
// rotate only one light, view is fixed
if (lightRotationMode != -1) {
float dx=(float)(newX-pos2DX)/wsize;
float dy=(float)(newY-pos2DY)/wsize;
// Cancels the rotation for the scene => use inverse of inverse...
if (!inverse) {
dx = -dx;
dy = -dy;
}
drotX.rotX(dy * 2 * (float)Math.PI);
drotY.rotY(dx * 2 * (float)Math.PI);
Matrix4f transform = new Matrix4f(rot);
transform.mul(drotX);
transform.mul(drotY);
Matrix4f tmp = new Matrix4f(rot);
tmp.invert();
transform.mul(tmp);
DirectionalLight light = scene.getLights()[lightRotationMode];
Vector3f direction = new Vector3f();
light.getDirection(direction);
transform.transform(direction);
light.setDirection(direction);
// this calls detach/attach => more flicker
//scene.showLightVector(direction.x,direction.y,direction.z);
// update pos, don't move view
pos2DX = newX;
pos2DY = newY;
peerWindow.repaint();
return;
}
// rotate all lights together with view
if (scene!=null) {
float dx=(float)(newX-pos2DX)/wsize;
float dy=(float)(newY-pos2DY)/wsize;
if (inverse) {
dx = -dx;
dy = -dy;
}
drotX.rotX(dy * 2 * (float)Math.PI);
drotY.rotY(dx * 2 * (float)Math.PI);
Matrix4f transform = new Matrix4f(rot);
transform.mul(drotX);
transform.mul(drotY);
Matrix4f tmp = new Matrix4f(rot);
tmp.invert();
transform.mul(tmp);
DirectionalLight[] lights = scene.getLights();
Vector3f direction = new Vector3f();
for (int i=0; i<lights.length; ++i) {
lights[i].getDirection(direction);
transform.transform(direction);
lights[i].setDirection(direction);
}
// don't update pos for super implementation call
}
super.rotate2D(newX,newY,inverse);
}
protected Timer zoomAccumulator = null;
protected int accumulator = 0;
/* (non-Javadoc)
* @see syn3d.ui.Transformator#zoom(int)
*/
public void zoom(int zoomIncrement) {
if (scene.getViewingMode() == SceneNode.ROTATION_MODE) {
super.zoom(zoomIncrement*5);
return;
}
synchronized (this) {
accumulator+=zoomIncrement;
if (zoomAccumulator!=null) return;
zoomAccumulator = new Timer();
zoomAccumulator.schedule(new TimerTask() {
public void run() {
int accuCopy;
synchronized (Frame3DJava3D.this) {
accuCopy = accumulator;
accumulator = 0;
zoomAccumulator = null;
}
Vector3f view = new Vector3f(0,0,-1);
rot.transform(view);
float scale = (float)Math.pow(2.0, Math.abs(accuCopy))*getSceneSize()/200.0f;
if (accuCopy<0) scale = -scale;
view.scale(scale);
trans.m03 += view.x;
trans.m13 += view.y;
trans.m23 += view.z;
applyTransform();
}
},200);
}
}
/* (non-Javadoc)
* @see syn3d.ui.Transformator#zoom2D(int, int)
*/
public void zoom2D(int newX, int newY) {
if (scene.getViewingMode() == SceneNode.ROTATION_MODE) {
super.zoom2D(newX, newY);
return;
}
float dx=(float)(newX-pos2DX)/wsize;
float dy=-(float)(newY-pos2DY)/wsize;
pos2DX = newX;
pos2DY = newY;
Vector3f viewX = new Vector3f(1,0,0);
rot.transform(viewX);
viewX.scale(dx/5.0f);
Vector3f viewY = new Vector3f(0,1,0);
rot.transform(viewY);
viewY.scale(dy/5.0f);
trans.m03 += viewX.x + viewY.x;
trans.m13 += viewX.y + viewY.y;
trans.m23 += viewX.z + viewY.z;
applyTransform();
}
/* (non-Javadoc)
* @see syn3d.ui.Transformator#translate2D(int, int)
*/
public void translate2D(int newX, int newY) {
if (scene.getViewingMode() == SceneNode.ROTATION_MODE) {
super.translate2D(newX, newY);
return;
}
// rotation around view vector
Vector3f view = new Vector3f(0,0,-1);
rot.transform(view);
float dx = (float)(newX-pos2DX)/wsize;
float dy = (float)(newY-pos2DY)/wsize;
pos2DX = newX;
pos2DY = newY;
AxisAngle4f aa = new AxisAngle4f(view, (dx-dy) * (float)Math.PI);
Matrix4f mat = new Matrix4f();
mat.setIdentity();
mat.setRotation(aa);
mat.mul(rot);
rot = mat;
applyTransform();
}
public void applyTransform(){
super.applyTransform();
if ((scene==null) || (scene.getViewingMode() == SceneNode.ROTATION_MODE)) {
transform.set(rot);
tempTransform.set(trans);
} else {
transform.set(trans);
tempTransform.set(rot);
}
transform.mul(tempTransform);
tempTransform.set(zoom);
transform.mul(tempTransform);
viewingPlatform.getViewPlatformTransform().setTransform(transform);
}
/**
* Adds or removes a single pick at the given position to the selected objects.
* @param posX the 2D X position where to do the picking
* @param posY the 2D Y position where to do the picking
* @return Returns the current selection, possibly an empy array
* @see ActiveNode.higlight(boolean,Object)
*/
public ArrayList toggleSinglePick(int posX, int posY) {
lastMouseX = posX;
lastMouseY = posY;
PickCanvas pickCanvas = new PickCanvas(canvas, scene.getBranchgroup());
pickCanvas.setMode(PickTool.GEOMETRY_INTERSECT_INFO);
pickCanvas.setTolerance(2.0f);
pickCanvas.setShapeLocation(posX, posY);
PickResult result = pickCanvas.pickClosest();
if (result!=null) {
Node node = result.getObject();
Object o = node.getUserData();
if (o instanceof ActiveNode) {
((ActiveNode)o).highlight(true,result);
lastPickSelection.add(o);
// pass full info to the active node so it can find which point is
// picked if necessary
lastPickSelection.add(result);
}
}
return lastPickSelection;
}
/**
* Adds or removes all picks between the given position and the last position,
* to the selected objects.
* @param posX the 2D X position defining a region with the last position. All objects in this region should be picked.
* @param posX the 2D Y position defining a region with the last position. All objects in this region should be picked.
* @return Returns the current selection, possibly an empy array
* @see ActiveNode.higlight(boolean,Object)
*/
public ArrayList toggleAllPicks(int posX, int posY) {
// do not update last mouse XY position, so user can refine selection from the same point
int dx = Math.abs(posX - lastMouseX) / 2;
int dy = Math.abs(posY - lastMouseY) / 2;
PickCanvas pickCanvas = new PickCanvas(canvas, scene.getBranchgroup());
pickCanvas.setMode(PickTool.GEOMETRY_INTERSECT_INFO);
pickCanvas.setTolerance(Math.max(dx,dy));
pickCanvas.setShapeLocation(posX, posY);
// TODO : don't do pick closest, loop on all results
PickResult result = pickCanvas.pickClosest();
if (result!=null) {
Node node = result.getObject();
Object o = node.getUserData();
if (o instanceof ActiveNode) {
((ActiveNode)o).highlight(true,result);
lastPickSelection.add(o);
// pass full info to the active node so it can find which point is
// picked if necessary
lastPickSelection.add(result);
}
}
return lastPickSelection;
}
/**
* Selects a single pick at the given position.
* @param posX the 2D X position where to do the picking
* @param posY the 2D Y position where to do the picking
* @return Returns the picked node or possibly a null object if there was nothing to pick at this position
* @see ActiveNode.higlight(boolean,Object)
*/
public ActiveNode pick(int posX, int posY) {
int s = lastPickSelection.size();
for (int i=0; i<s; i+=2) {
ActiveNode node = (ActiveNode)lastPickSelection.get(i);
node.highlight(false,lastPickSelection.get(i+1));
}
lastPickSelection.clear();
toggleSinglePick(posX,posY);
if (lastPickSelection.size()>0) return (ActiveNode)lastPickSelection.get(0);
return null;
}
/*
*
*/
protected float getSceneSize() {
BranchGroup bg = scene.getBranchgroup();
boolean autocompute = bg.getBoundsAutoCompute();
scene.getBranchgroup().setBoundsAutoCompute(true);
Bounds bounds = scene.getBranchgroup().getBounds();
scene.getBranchgroup().setBoundsAutoCompute(autocompute);
if (bounds instanceof BoundingSphere) {
return (float)((BoundingSphere)bounds).getRadius();
}
// else, include this bounds object in a bounding sphere and get radius
return 0;
}
/**
* Main function to test navigation
* @param args
*/
public static void main(String[] args) {
PluginManager pluginManager = (PluginManager) new Java3DPluginManager();
SceneNodeJava3D scene = new SceneNodeJava3D(null, pluginManager);
//BoundingSphere bs = new BoundingSphere(//universe bounds
// new Point3d(0.0, 0.0, 0.0), 1000.0);
//PlatformGeometry pg = new PlatformGeometry();
//Color3f ac = new Color3f(0.2f, 0.2f, 0.2f);
//AmbientLight al = new AmbientLight(ac);
//al.setInfluencingBounds(bs);
//pg.addChild(al);
//f1.viewingPlatform.setPlatformGeometry(pg);
// TODO check where to add the light
//f1.viewingPlatform.setNominalViewingTransform();
BranchGroup objRoot = scene.getBranchgroup();
Appearance a = new Appearance();
a.setPolygonAttributes(new PolygonAttributes(PolygonAttributes.POLYGON_LINE, PolygonAttributes.CULL_NONE, 0));
TransformGroup sphereTrans = new TransformGroup();
sphereTrans.addChild(new Sphere(1.f, Sphere.GENERATE_NORMALS,40, a));
scene.detach();
objRoot.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
objRoot.addChild(sphereTrans);
AmbientLight aLgt = new AmbientLight(new Color3f(0.2f, 0.2f, 0.2f));
objRoot.addChild(aLgt);
scene.attach();
}
}