package org.andork.j3d.camera;
import java.util.ArrayList;
import java.util.List;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.Light;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.vecmath.Color3f;
import javax.vecmath.Matrix3d;
import javax.vecmath.Matrix3f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Tuple3d;
import javax.vecmath.Tuple3f;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;
import com.sun.j3d.utils.universe.ViewingPlatform;
public class Camera3D
{
private TransformGroup m_lightGroup;
private Light m_headlight = null;
private final ViewingPlatform m_platform;
private final TransformGroup m_transformGroup;
private final Transform3D m_initXform = new Transform3D( );
private final Transform3D m_tempXform = new Transform3D( );
private final Transform3D m_transform = new Transform3D( );
private final CameraPosition m_currentPosition = new CameraPosition( );
private final CameraPosition m_pendingPosition = new CameraPosition( );
public static final Point3d RESET_LOCATION = new Point3d( 0 , 2000 , 0 );
public static final Point3d RESET_LOOKAT = new Point3d( 0 , 0 , 0 );
private BoundingSphere m_restrictionBounds = null;
private static final CameraPosition RESET_POSITION = new CameraPosition( RESET_LOCATION , RESET_LOOKAT );
public Camera3D( ViewingPlatform viewPlatform )
{
m_platform = viewPlatform;
m_transformGroup = m_platform.getViewPlatformTransform( );
m_initXform.rotY( -Math.PI / 2 );
m_tempXform.rotZ( Math.PI / 2 );
m_initXform.mul( m_tempXform );
m_initXform.setIdentity( );
importVPT( );
// TvdProperties.getInstance().setProperty(TvdProperties.TVD_HEADLIGHT_ON_KEY, "true");
createHeadlight( );
setHeadlightOn( true );
reset( );
}
/**
* Imports the view platform transform into the current and pending positions.
*/
private void importVPT( )
{
m_transformGroup.getTransform( m_transform );
m_tempXform.invert( m_initXform );
m_transform.mul( m_tempXform ); // remove initial transform from ViewingPlatform transform
m_currentPosition.setTransform( m_transform );
m_pendingPosition.setTransform( m_transform );
}
/**
* Restricts the pending position to the restriction bounds, copies the pending position to the current position, and updates the view platform transform.
*/
private void exportVPT( )
{
if( m_restrictionBounds != null )
{
m_pendingPosition.restrict( m_restrictionBounds );
}
m_pendingPosition.getTransform( m_transform );
m_transform.mul( m_initXform );
try
{
m_transformGroup.setTransform( m_transform );
}
catch( Throwable t )
{
t.printStackTrace( );
System.out.println( "Current position: " + m_currentPosition );
System.out.println( "Pending position: " + m_pendingPosition );
}
m_currentPosition.copy( m_pendingPosition );
fireOrientationChangedEvent( new CameraEvent( this ) );
}
public void reset( )
{
setLocation( RESET_LOCATION , false );
lookAt( RESET_LOOKAT , true );
}
public Point3f getResetLocation( Point3f result )
{
return RESET_POSITION.getLocation( result );
}
public Point3f getResetLookAt( Point3f result )
{
return RESET_POSITION.getLookAt( result );
}
public Point3d getResetLocation( Point3d result )
{
return RESET_POSITION.getLocation( result );
}
public Point3d getResetLookAt( Point3d result )
{
return RESET_POSITION.getLookAt( result );
}
/**
* Gets the zone the camera is prevented from moving out of. If <code>null</code>, there is no restriction.
*/
public BoundingSphere getRestrictionBounds( )
{
return m_restrictionBounds;
}
/**
* Sets the zone the camera is prevented from moving out of. If <code>null</code>, there is no restriction.
*/
public void setRestrictionBounds( BoundingSphere bounds )
{
CameraPosition.checkValid( bounds );
m_restrictionBounds = bounds;
}
/**
* Sets the camera location to <code>newLocation</code> without affecting the rotation.
*/
public void setLocation( Tuple3d newLocation , boolean applyNow )
{
m_pendingPosition.setLocation( newLocation );
if( applyNow )
{
exportVPT( );
}
}
/**
* Sets the camera location to <code>newLocation</code> without affecting the rotation.
*/
public void setLocation( Tuple3f newLocation , boolean applyNow )
{
m_pendingPosition.setLocation( newLocation );
if( applyNow )
{
exportVPT( );
}
}
/**
* Pans and tilts the camera to point at <code>lookAt</code>.
*
* @see #setPan(double)
* @see #setTilt(double)
*/
public void lookAt( Tuple3d lookAt , boolean applyNow )
{
m_pendingPosition.lookAt( lookAt );
if( applyNow )
{
exportVPT( );
}
}
/**
* Pans and tilts the camera to point at <code>lookAt</code>.
*
* @see #setPan(double)
* @see #setTilt(double)
*/
public void lookAt( Tuple3f lookAt , boolean applyNow )
{
m_pendingPosition.lookAt( lookAt );
if( applyNow )
{
exportVPT( );
}
}
/**
* Moves the camera to the specified location and pan and tilts it to point at <code>lookAt</code>.
*
* @see #setPan(double)
* @see #setTilt(double)
*/
public void lookAt( Tuple3d loc , Tuple3d lookAt , boolean applyNow )
{
m_pendingPosition.setLocation( loc );
m_pendingPosition.lookAt( lookAt );
if( applyNow )
{
exportVPT( );
}
}
/**
* Moves the camera to the specified location and pan and tilts it to point at <code>lookAt</code>.
*
* @see #setPan(double)
* @see #setTilt(double)
*/
public void lookAt( Tuple3f loc , Tuple3f lookAt , boolean applyNow )
{
m_pendingPosition.setLocation( loc );
m_pendingPosition.lookAt( lookAt );
if( applyNow )
{
exportVPT( );
}
}
/**
* Places the location of the camera in <code>result</code> and returns it.
*/
public Point3d getLocation( Point3d result )
{
return m_pendingPosition.getLocation( result );
}
/**
* Places the location of the camera in <code>result</code> and returns it.
*/
public Point3f getLocation( Point3f result )
{
return m_pendingPosition.getLocation( result );
}
/**
* Places the location of the camera in <code>result</code> and returns it.
*/
public Vector3d getLocation( Vector3d result )
{
return m_pendingPosition.getLocation( result );
}
/**
* Places the location of the camera in <code>result</code> and returns it.
*/
public Vector3f getLocation( Vector3f result )
{
return m_pendingPosition.getLocation( result );
}
public Point3d getLookAt( Point3d result )
{
return m_pendingPosition.getLookAt( result );
}
public Point3f getLookAt( Point3f result )
{
return m_pendingPosition.getLookAt( result );
}
/**
* Translates the camera.
*
* @param translation
* offset in virtual world coordinates
*/
public void translate( Vector3d translation , boolean applyNow )
{
m_pendingPosition.translate( translation );
if( applyNow )
{
exportVPT( );
}
}
/**
* Translates the camera.
*
* @param translation
* offset in virtual world coordinates
*/
public void translate( Vector3f translation , boolean applyNow )
{
m_pendingPosition.translate( translation );
if( applyNow )
{
exportVPT( );
}
}
/**
* Translates the camera.
*
* @param dx
* x offset in virtual world coordinates
* @param dy
* y offset in virtual world coordinates
* @param dz
* z offset in virtual world coordinates
*/
public void translate( double dx , double dy , double dz , boolean applyNow )
{
m_pendingPosition.translate( dx , dy , dz );
if( applyNow )
{
exportVPT( );
}
}
/**
* Moves the camera relative to its orientation.
*
* @param dForward
* distance to move forward
* @param dRight
* distance to move to the right
* @param dDown
* distance to move downward
*/
public void move( double dForward , double dRight , double dDown , boolean applyNow )
{
m_pendingPosition.move( dForward , dRight , dDown );
if( applyNow )
{
exportVPT( );
}
}
/**
* Moves the camera relative to its orientation.
*
* @param motion
* vector specifying distances to move (forward, right, down)
*/
public void move( Vector3d motion , boolean applyNow )
{
m_pendingPosition.move( motion );
if( applyNow )
{
exportVPT( );
}
}
/**
* Moves the camera relative to its orientation.
*
* @param motion
* vector specifying distances to move (forward, right, down)
*/
public void move( Vector3f motion , boolean applyNow )
{
m_pendingPosition.move( motion );
if( applyNow )
{
exportVPT( );
}
}
/**
* Sets the camera pan angle to <code>pan</code>. Pan is defined as rotation around the Z axis after tilt and roll have been applied.
*
* @see #pan(double)
* @see #setTilt(double)
* @see #setRoll(double)
*/
public void setPan( double pan , boolean applyNow )
{
m_pendingPosition.setPan( pan );
if( applyNow )
{
exportVPT( );
}
}
/**
* Increments the camera pan angle by <code>inc</code>. Pan is defined as rotation around the Z axis after tilt and roll have been applied.
*
* @see #setPan(double)
* @see #tilt(double)
* @see #roll(double)
*/
public void pan( double inc , boolean applyNow )
{
m_pendingPosition.pan( inc );
if( applyNow )
{
exportVPT( );
}
}
/**
* Sets the camera tilt angle to <code>tilt</code>. Tilt is defined as rotation around the Y axis before pan has been applied and after roll has been
* applied. <b>Note:</b> the tilt is clamped to the range (-{@link #MAX_TILT}, {@link #MAX_TILT}) to prevent gimbal lock at the up/down singularities.
*
* @see #tilt(double)
* @see #setPan(double)
* @see #setRoll(double)
*/
public void setTilt( double tilt , boolean applyNow )
{
m_pendingPosition.setTilt( tilt );
if( applyNow )
{
exportVPT( );
}
}
/**
* Increments the camera tilt angle by <code>inc</code>. Tilt is defined as rotation around the Y axis before pan has been applied and after roll has been
* applied. <b>Note:</b> the tilt is clamped to the range (-{@link #MAX_TILT}, {@link #MAX_TILT}) to prevent gimbal lock at the up/down singularities.
*
* @see #setTilt(double)
* @see #pan(double)
* @see #roll(double)
*/
public void tilt( double inc , boolean applyNow )
{
m_pendingPosition.tilt( inc );
if( applyNow )
{
exportVPT( );
}
}
/**
* Sets the camera roll angle to <code>roll</code>. Roll is defined as rotation around the X axis before pan and tilt are applied.
*
* @see #roll(double)
* @see #setPan(double)
* @see #setTilt(double)
*/
public void setRoll( double roll , boolean applyNow )
{
m_pendingPosition.setRoll( roll );
if( applyNow )
{
exportVPT( );
}
}
/**
* Increments the camera roll angle by <code>inc</code>. Roll is defined as rotation around the X axis before pan and tilt are applied.
*
* @see #setRoll(double)
* @see #pan(double)
* @see #tilt(double)
*/
public void roll( double inc , boolean applyNow )
{
m_pendingPosition.roll( inc );
if( applyNow )
{
exportVPT( );
}
}
/**
* Sets the pan and tilt simultaneously.
*
* @see #setPan(double)
* @see #setTilt(double)
*/
public void setPanTilt( double pan , double tilt , boolean applyNow )
{
m_pendingPosition.setPanTilt( pan , tilt );
if( applyNow )
{
exportVPT( );
}
}
/**
* Increments the pan and tilt simultaneously.
*
* @see #pan(double)
* @see #tilt(double)
*/
public void panTilt( double panInc , double tiltInc , boolean applyNow )
{
m_pendingPosition.panTilt( panInc , tiltInc );
if( applyNow )
{
exportVPT( );
}
}
/**
* Sets the pan, tilt, and roll simultaneously.
*
* @see #setPan(double)
* @see #setTilt(double)
* @see #setRoll(double)
*/
public void setPanTiltRoll( double pan , double tilt , double roll , boolean applyNow )
{
m_pendingPosition.setPanTiltRoll( pan , tilt , roll );
if( applyNow )
{
exportVPT( );
}
}
/**
* Gets the camera pan angle, which is defined as rotation around the Z axis after tilt and roll have been applied.
*
* @see #setPan(double)
* @see #getTilt(double)
* @see #getRoll(double)
*/
public double getPan( )
{
return m_pendingPosition.getPan( );
}
/**
* Gets the camera tilt angle, which is defined as rotation around the Y axis before pan is applied and after roll has been applied.
*
* @see #setTilt(double)
* @see #getPan(double)
* @see #getRoll(double)
*/
public double getTilt( )
{
return m_pendingPosition.getTilt( );
}
/**
* Gets the camera roll angle, which is defined as rotation around the X axis before pan and tilt are applied.
*
* @see #setRoll(double)
* @see #getPan(double)
* @see #getTilt(double)
*/
public double getRoll( )
{
return m_pendingPosition.getRoll( );
}
/**
* Places the rotational component of the camera transform in <code>result</code> and returns it.
*/
public Matrix3d getRotation( Matrix3d result )
{
return m_pendingPosition.getRotation( result );
}
/**
* Places the rotational component of the camera transform in <code>result</code> and returns it.
*/
public Matrix3f getRotation( Matrix3f result )
{
return m_pendingPosition.getRotation( result );
}
/**
* Sets <code>result</code> to point forward from the camera and returns it.
*/
public Vector3d getForward( Vector3d result )
{
return m_pendingPosition.getForward( result );
}
/**
* Sets <code>result</code> to point backward from the camera and returns it.
*/
public Vector3d getBackward( Vector3d result )
{
return m_pendingPosition.getBackward( result );
}
/**
* Sets <code>result</code> to point to the right of camera and returns it.
*/
public Vector3d getRight( Vector3d result )
{
return m_pendingPosition.getRight( result );
}
/**
* Sets <code>result</code> to point to the left of camera and returns it.
*/
public Vector3d getLeft( Vector3d result )
{
return m_pendingPosition.getLeft( result );
}
/**
* Sets <code>result</code> to point down from the camera and returns it.
*/
public Vector3d getDown( Vector3d result )
{
return m_pendingPosition.getDown( result );
}
/**
* Sets <code>result</code> to point up from the camera and returns it.
*/
public Vector3d getUp( Vector3d result )
{
return m_pendingPosition.getUp( result );
}
/**
* Sets <code>result</code> to point forward from the camera and returns it.
*/
public Vector3f getForward( Vector3f result )
{
return m_pendingPosition.getForward( result );
}
/**
* Sets <code>result</code> to point backward from the camera and returns it.
*/
public Vector3f getBackward( Vector3f result )
{
return m_pendingPosition.getBackward( result );
}
/**
* Sets <code>result</code> to point to the right of camera and returns it.
*/
public Vector3f getRight( Vector3f result )
{
return m_pendingPosition.getRight( result );
}
/**
* Sets <code>result</code> to point to the left of camera and returns it.
*/
public Vector3f getLeft( Vector3f result )
{
return m_pendingPosition.getLeft( result );
}
/**
* Sets <code>result</code> to point down from the camera and returns it.
*/
public Vector3f getDown( Vector3f result )
{
return m_pendingPosition.getDown( result );
}
/**
* Sets <code>result</code> to point up from the camera and returns it.
*/
public Vector3f getUp( Vector3f result )
{
return m_pendingPosition.getUp( result );
}
/**
* Places the pending camera position in <code>position</code> and returns it.
*/
public CameraPosition getPosition( CameraPosition position )
{
position.copy( m_pendingPosition );
return position;
}
/**
* Sets the camera position to <code>newPosition</code>.
*/
public void setPosition( CameraPosition newPosition , boolean applyNow )
{
m_pendingPosition.copy( newPosition );
if( applyNow )
{
exportVPT( );
}
}
public Point3d localToVworld( Point3d p )
{
m_pendingPosition.localToVworld( p );
return p;
}
public Point3f localToVworld( Point3f p )
{
m_pendingPosition.localToVworld( p );
return p;
}
public Vector3d localToVworld( Vector3d v )
{
m_pendingPosition.localToVworld( v );
return v;
}
public Vector3f localToVworld( Vector3f v )
{
m_pendingPosition.localToVworld( v );
return v;
}
public Point3d vworldToLocal( Point3d p )
{
m_pendingPosition.vworldToLocal( p );
return p;
}
public Point3f vworldToLocal( Point3f p )
{
m_pendingPosition.vworldToLocal( p );
return p;
}
public Vector3d vworldToLocal( Vector3d v )
{
m_pendingPosition.vworldToLocal( v );
return v;
}
public Vector3f vworldToLocal( Vector3f v )
{
m_pendingPosition.vworldToLocal( v );
return v;
}
protected void createHeadlight( )
{
final Color3f lightColor = new Color3f( 0.9f , 0.9f , 0.9f );
final DirectionalLight light = new DirectionalLight( );
light.setCapability( Light.ALLOW_STATE_WRITE );
light.setColor( lightColor );
final BoundingSphere worldBounds = new BoundingSphere( new Point3d( 0.0 , 0.0 , 0.0 ) , 100000.0 ); // Center, Extent
light.setInfluencingBounds( worldBounds );
m_headlight = light;
final BranchGroup bg = new BranchGroup( );
m_lightGroup = new TransformGroup( );
m_lightGroup.setCapability( TransformGroup.ALLOW_TRANSFORM_WRITE );
bg.addChild( m_lightGroup );
m_lightGroup.addChild( m_headlight );
m_transformGroup.addChild( bg );
}
public void setHeadlightOn( boolean value )
{
if( m_headlight != null )
{
m_headlight.setEnable( value );
}
}
public boolean isHeadlightOn( )
{
return m_headlight.getEnable( );
}
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CONFIGURATION
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////////////////////////////////////////////
// LISTENERS
// //////////////////////////////////////////////////////////////////////////////////////////////////////////
List<CameraListener> m_listeners = new ArrayList<CameraListener>( );
public void addCameraListener( CameraListener listener )
{
m_listeners.add( listener );
}
public void removeCameraListener( CameraListener listener )
{
m_listeners.remove( listener );
}
private void fireOrientationChangedEvent( CameraEvent event )
{
for( final CameraListener listener : m_listeners )
{
try
{
listener.orientationChanged( event );
}
catch( final Throwable t )
{
t.printStackTrace( );
}
}
}
public void transform( Transform3D xform , boolean applyNow )
{
m_pendingPosition.transform( xform );
if( applyNow )
{
exportVPT( );
}
}
}