/***************************************************************************** * J3D.org Copyright (c) 2000 * Java Source * * This source is licensed under the GNU LGPL v2.1 * Please read http://www.gnu.org/copyleft/lgpl.html for more information * * This software comes with the standard NO WARRANTY disclaimer for any * purpose. Use it at your own risk. If there's a problem you get to fix it. * ****************************************************************************/ package org.j3d.ui.navigation; // Standard imports import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.Timer; import javax.media.j3d.*; import javax.vecmath.*; // Application specific imports // none /** * This class will create smooth transitions from one viewpoint to another. *
*
* The transition effects start when a new set of transform groups are used to
* drive the view. To provide the driving factor, a Swing Timer object is used
* rather than using the Java 3D behaviour system.
*
* @author Halden VR Centre, Institute for Energy Technology
* Updated for j3d.org by Justin Couch
* @version $Revision $
*/
public class ViewpointTransition implements ActionListener
{
/** The view that we are moving about. */
private View view;
/** The transform group above the view that is being moved each frame */
private TransformGroup viewTg;
/** A timer that drives our updates to the screen */
private Timer timer;
/** The time that this current transition is to take in milliseconds */
private int totalTimeMS;
/** The end time, in epoch coordinates, of the end of the transition */
private long epochEndTime;
/** Working calculation about how far along the transition we are */
private double alfa;
/** A delay between callbacks from the time for frames */
private int timerDelay = 0;
// The following are working variables that we want to create once and use
// all the time. This saves on GC costs and speeds up the process quite
// substantially.
// The names are
// for the current working copy
// 1 for the value at the start of transition
// 2 for the value at the end of the transition
private Point3d eye = new Point3d();
private Point3d eye1 = new Point3d();
private Point3d eye2 = new Point3d();
private Point3d center = new Point3d();
private Point3d center1 = new Point3d();
private Point3d center2 = new Point3d();
private Vector3d up = new Vector3d();
private Vector3d up1 = new Vector3d();
private Vector3d up2 = new Vector3d();
private Vector3d location1 = new Vector3d();
private Vector3d location2 = new Vector3d();
private Vector3d direction1 = new Vector3d();
private Vector3d direction2 = new Vector3d();
private Transform3D previousFrameTx = new Transform3D();
private Transform3D currentTx = new Transform3D();
private Transform3D destinationTx = new Transform3D();
/** An observer for information about updates for this transition */
private FrameUpdateListener updateListener;
/**
* Construct a new transition object ready to work.
*/
public ViewpointTransition()
{
timer = new Timer(100, this);
timer.setInitialDelay(0);
timer.setRepeats(true);
timer.setLogTimers(false);
timer.setCoalesce(true);
timer.stop();
}
/**
* Set the listener for frame update notifications. By setting a value of
* null it will clear the currently set instance
*
* @param l The listener to use for this transition
*/
public void setFrameUpdateListener(FrameUpdateListener l)
{
updateListener = l;
}
/**
* Transition between two locations represented by the initial
* TranformGroup and the destination transform information starting
* immediately.
*
* @param view The view that is associated with this transform
* @param viewTg is the transformgroup to be transitioned that holds
* the view.
* @param endTx is the final state to be transitioned to
* @param totalTime The time to be spent with this transition
* (in miliseconds)
*/
public void transitionTo(View view,
TransformGroup viewTg,
Transform3D endTx,
int totalTime)
{
this.view = view;
this.viewTg = viewTg;
destinationTx = new Transform3D(endTx);
totalTimeMS = totalTime;
epochEndTime = System.currentTimeMillis() + totalTime;
timer.start();
// Set up our internal transforms that we will be doing the morphing
// along.
viewTg.getTransform(currentTx);
currentTx.get(location1);
eye1.set(location1);
direction1.set(0,0,-1);
currentTx.transform(direction1);
center1.add(eye1,direction1);
up1.set(0,1,0);
currentTx.transform(up1);
// Make sure the destination transform is set up for the eye position
destinationTx.get(location2);
eye2.set(location2);
direction2.set(0,0,-1);
destinationTx.transform(direction2);
center2.add(eye2,direction2);
up2.set(0,1,0);
destinationTx.transform(up2);
}
/**
* Process an action event from the timer. This event is only for the time
* and should not be associated with any other sort of action event like
* menu callbacks.
*
* @param evt The event that caused this action to be called
*/
public void actionPerformed(ActionEvent evt)
{
viewTg.getTransform(previousFrameTx);
// If not equal, then someone was changing it so there is no point
// in further transition.
if(currentTx.equals(previousFrameTx))
{
// Hmmm. Magic number. No idea what the value 10 is for.
timerDelay = 10 + (int)view.getLastFrameDuration() / 2;
timer.setDelay(timerDelay);
// How far into the transitiion are we?
alfa= 1 - ((double)(epochEndTime - System.currentTimeMillis()) /
totalTimeMS);
if(alfa > 1)
alfa = 1;
// Build the interpolated UVN camera coordinates
eye.interpolate(eye1, eye2, alfa);
center.interpolate(center1, center2, alfa);
up.interpolate(up1, up2, alfa);
// Setup the current transform position. Always normalise
// otherwise it will grow non-congurent.
currentTx.lookAt(eye,center,up);
currentTx.invert();
currentTx.normalize();
try
{
viewTg.setTransform(currentTx);
}
catch(Exception e)
{
// If the set has screwed up then just set the value to the
// final value and terminate the transition.
System.out.println("Transition stopping due to invalid value");
currentTx.set(destinationTx);
viewTg.setTransform(currentTx);
alfa=9;
//e.printStackTrace();
}
if(updateListener!=null)
updateListener.viewerPositionUpdated(currentTx);
}
else
alfa=9;
// Should we now stop if the change is greater than the end of the
// timescale provided.
if(alfa >= 1)
{
timer.stop();
if(updateListener != null)
updateListener.transitionEnded(currentTx);
}
}
}