/*
 * $RCSfile: View.java,v $
 *
 * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
 *
 * Use is subject to license terms.
 *
 * $Revision: 1.12 $
 * $Date: 2007/04/12 17:34:07 $
 * $State: Exp $
 */

package javax.media.j3d;

import javax.vecmath.*;
import java.util.Vector;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Iterator;
import java.util.Enumeration;
import java.awt.*;
import com.sun.j3d.utils.universe.Viewer; // Needed for Support of DVR.

/**
 * The View object contains all parameters needed in rendering a
 * three dimensional scene from one viewpoint.  A view contains a list
 * of Canvas3D objects that the view is rendered into.  It exists outside
 * of the scene graph, but attaches to a ViewPlatform leaf node object
 * in the scene graph.  It also contains a reference to a PhysicalBody
 * and a PhysicalEnvironment object.
 * <P>
 * The View object is the main Java 3D object for controlling the
 * Java 3D viewing model. All of the components that specify the
 * view transform used to render to the 3D canvases are either contained
 * in the View object or in objects that are referenced by the View
 * object.
 * <P>
 * Java 3D allows applications to specify multiple simultaneously active
 * View objects, each controlling its own set of canvases.
 * <P>
 * The Java 3D View object has several instance variables and methods,
 * but most are calibration variables or user-helping functions. The
 * viewing policies defined by the View object are described below.
 * <P>
 * <b>Policies</b><P>
 *
 * The View object defines the following policies:<P>
 * <UL>
 * <LI>View policy - informs Java 3D whether it should generate
 * the view using the head-tracked system of transformations or the
 * head-mounted system of transformations. These policies are attached
 * to the Java 3D View object. There are two view policies:</LI><P>
 * <UL>
 * <LI>SCREEN_VIEW - specifies that Java 3D should compute a new
 * viewpoint using the sequence of transforms appropriate to screen-based
 * head-tracked display environments (fish-tank VR/portals/VR-desks).
 * This is the default setting.</LI><P>
 * <LI>HMD_VIEW - specifies that Java 3D should compute a new viewpoint
 * using the sequence of transforms appropriate to head mounted display
 * environments. This policy is not available in compatibility mode
 * (see the setCompatibilityModeEnable method description).</LI><P>
 * </UL>
 * <LI>Projection policy - specifies whether Java 3D should generate
 * a parallel projection or a perspective projection. This policy
 * is attached to the Java 3D View object. There are two projection
 * policies:</LI><P>
 * <UL>
 * <LI>PARALLEL_PROJECTION - specifies that a parallel projection
 * transform is computed.</LI><P>
 * <LI>PERSPECTIVE_PROJECTION - specifies that a perspective projection
 * transform is computed. This is the default policy.</LI><P>
 * </UL>
 * <LI>Screen scale policy - specifies where the screen scale comes from.
 * There are two screen scale policies:</LI><P>
 * <UL>
 * <LI>SCALE_SCREEN_SIZE - specifies that the scale is derived from the
 * physical screen according to the following formula (this is the
 * default mode):</LI>
 * <UL>
 * <code>screenScale = physicalScreenWidth / 2.0</code><P>
 * </UL>
 * <LI>SCALE_EXPLICIT - pecifies that the scale is taken directly from
 * the user-provided <code>screenScale</code> attribute (see the
 * setScreenScale method description).</LI><P>
 * </UL>
 * <LI>Window resize policy - specifies how Java 3D modifies the view
 * when users resize windows. When users resize or move windows,
 * Java 3D can choose to think of the window as attached either to
 * the physical world or to the virtual world. The window resize
 * policy allows an application to specify how the
 * view model will handle resizing requests.
 * There are two window resize policies:</LI><P>
 * <UL>
 * <LI>VIRTUAL_WORLD - implies that the original image remains the
 * same size on the screen but the user sees more or less of the
 * virtual world depending on whether the window grew or shrank
 * in size.</LI><P>
 * <LI>PHYSICAL_WORLD - implies that the original image continues
 * to fill the window in the same way using more or less pixels
 * depending on whether the window grew or shrank in size.</LI><P>
 * </UL>
 * <LI>Window movement policy - specifies what part of the virtual
 * world Java 3D draws as a function of window placement on the screen.
 * There are two window movement policies:</LI><P>
 * <UL>
 * <LI>VIRTUAL_WORLD - implies that the image seen in the window
 * changes as the position of the window shifts on the screen.
 * (This mode acts as if the window were a window into the virtual
 * world.)</LI><P>
 * <LI>PHYSICAL_WORLD - implies that the image seen in the window
 * remains the same no matter where the user positions the window
 * on the screen.</LI><P>
 * </UL>
 * <LI>Window eyepoint policy - comes into effect in a non-head-tracked
 * environment. The policy tells Java 3D how to construct a new view
 * frustum based on changes in the field of view and in the Canvas3D's
 * location on the screen. The policy only comes into effect when the
 * application changes a parameter that can change the placement of the
 * eyepoint relative to the view frustum.
 * There are three window eyepoint policies:</LI><P>
 * <UL>
 * <LI>RELATIVE_TO_SCREEN - tells Java 3D to interpret the eye's position
 * relative to the entire screen. No matter where an end user moves a
 * window (a Canvas3D), Java 3D continues to interpret the eye's position
 * relative to the screen. This implies that the view frustum changes shape
 * whenever an end user moves the location of a window on the screen.
 * In this mode, the field of view is read-only.</LI><P>
 * <LI>RELATIVE_TO_WINDOW - specifies that Java 3D should interpret the
 * eye's position information relative to the window (Canvas3D). No matter
 * where an end user moves a window (a Canvas3D), Java 3D continues to
 * interpret the eye's position relative to that window. This implies
 * that the frustum remains the same no matter where the end user
 * moves the window on the screen. In this mode, the field of view
 * is read-only.</LI><P>
 * <LI>RELATIVE_TO_FIELD_OF_VIEW - tells Java 3D that it should
 * modify the eyepoint position so it is located at the appropriate
 * place relative to the window to match the specified field of view.
 * This implies that the view frustum will change whenever the
 * application changes the field of view. In this mode, the eye
 * position is read-only. This is the default setting.</LI><P>
 * <LI>RELATIVE_TO_COEXISTENCE - tells Java 3D to interpret the eye's
 * position in coexistence coordinates. In this mode, the eye position
 * is taken from the view (rather than the Canvas3D) and transformed from
 * coexistence coordinates to image plate coordinates for each
 * Canvas3D.  The resulting eye position is relative to the screen. As
 * in RELATIVE_TO_SCREEN mode, this implies that the view frustum
 * changes shape whenever an end user moves the location of a window
 * on the screen.  In this mode, the field of view is
 * read-only.</LI><P>
 * </UL>
 * <LI>Front and back clip policies - specifies how Java 3D
 * interprets clipping distances to both the near and far clip
 * planes. The policies can contain one of four values specifying
 * whether a distance measurement should be interpreted in
 * the physical or the virtual world and whether that distance
 * measurement should be interpreted relative to the physical
 * eyepoint or the physical screen.
 * The front and back clip policies
 * are specified separately. The front clip policy determines
 * where Java 3D places the front clipping plane. The back clip
 * policy determines where Java 3D places the back clipping plane.
 * The values for both front and back clipping planes are:</LI><P>
 * <UL>
 * <LI>VIRTUAL_EYE - specifies that the associated distance is from
 * the eye and in units of virtual distance.</LI><P>
 * <LI>PHYSICAL_EYE - specifies that the associated distance is from
 * the eye and in units of physical distance (in meters).
 * This is the default policy for both front and back clipping.</LI><P>
 * <LI>VIRTUAL_SCREEN  - specifies that the associated distance is
 * from the screen and in units of virtual distance. </LI><P>
 * <LI>PHYSICAL_SCREEN - specifies that the associated distance is
 * from the screen and in units of physical distance (in meters).
 * </LI><P>
 * </UL>
 * <LI>Visibility policy - specifies how visible and invisible objects
 * are drawn. There are three visibility policies:</LI><P>
 * <UL>
 * <LI>VISIBILITY_DRAW_VISIBLE - only visible objects are drawn
 * (this is the default).</LI><P>
 * <LI>VISIBILITY_DRAW_INVISIBLE - only invisible objects are drawn.</LI><P>
 * <LI>VISIBILITY_DRAW_ALL - both visible and invisible
 * objects are drawn. </LI><P>
 * </UL>
 * <LI>Transparency sorting policy - specifies whether and how
 * transparent objects are sorted.  Sorting multiple transparent
 * objects is necessary to avoid artifacts caused by overlapping
 * transparent objects.  There are two transparency sorting
 * policies:</LI><P>
 * <UL>
 * <LI>TRANSPARENCY_SORT_NONE - no depth sorting of transparent
 * objects is performed (this is the default).  Transparent objects
 * are drawn after opaque objects, but are not sorted from back to
 * front.</LI><P>
 * <LI>TRANSPARENCY_SORT_GEOMETRY - transparent objects are
 * depth-sorted on a per-geometry basis.  Each geometry object of each
 * transparent Shape3D node is drawn from back to front.  Note that
 * this policy will not split geometry into smaller pieces, so
 * intersecting or intertwined objects may not be sorted
 * correctly.  The method used for determining which geometry is closer
 * is implementation dependent.</LI><P>
 * </UL>
 * </UL>
 * <b>Projection and Clip Parameters</b><P>
 * The projection and clip parameters determine the view model's
 * field of view and the front and back clipping distances.<P>
 * <UL>
 * <LI>Field of view - specifies the view model's horizontal
 * field of view in radians, when in the default non-head-tracked
 * mode. This value is ignored when the view model is operating
 * in head-tracked mode, or when the Canvas3D's window eyepoint
 * policy is set to a value other than the default setting of
 * RELATIVE_TO_FIELD_OF_VIEW.</LI><P>
 * <LI>Front clip distance - specifies the distance away from the
 * clip origin, specified by the front clip policy variable, in
 * the direction of gaze where objects stop disappearing. Objects
 * closer than the clip origin (eye or screen)
 * plus the front clip distance are not drawn. Measurements are
 * done in the space (physical or virtual) that is specified by
 * the associated front clip policy parameter.</LI><P>
 * <LI>Back clip distance - specifies the distance away from the
 * clip origin (specified by the back clip policy variable) in the
 * direction of gaze where objects begin disappearing. Objects
 * farther away from the clip origin (eye or
 * screen) plus the back clip distance are not drawn.
 * Measurements are done in the space (physical or virtual) that
 * is specified by the associated back clip policy
 * parameter. The View object's back clip distance is ignored
 * if the scene graph contains an active Clip leaf node.</LI><P>
 * There are several considerations to take into account when
 * choosing values for the front and back clip distances.<P>
 * <UL>
 * <LI>The front clip distance must be greater than 0.0 in physical
 * eye coordinates.</LI><P>
 * <LI>The front clipping plane must be in front of the back clipping
 * plane, that is, the front clip distance must be less than the
 * back clip distance in physical eye coordinates.</LI><P>
 * <LI>The front and back clip distances, in physical eye coordinates,
 * must be less than the largest positive single-precision floating
 * point value, Float.MAX_VALUE. In practice, since these physical
 * eye coordinate distances are in meters, the values
 * should be much less than that.</LI><P>
 * <LI>The ratio of the back distance divided by the front distance,
 * in physical eye coordinates, affects Z-buffer precision. This ratio
 * should be less than about 3000 to accommodate 16-bit Z-buffers.
 * Values of 100 to less than 1000 will produce better results.</LI><P>
 * </UL>
 * Violating any of the above rules will result in undefined behavior.
 * In many cases, no picture will be drawn.<P>
 * </UL>
 * <b>Frame Start Time, Duration, and Number</b><P>
 *
 * There are five methods used to get information about system
 * execution and performance:<P>
 * <UL>
 * <code>getCurrentFrameStartTime</code> returns the time at which 
 * the most recent rendering frame started.<P>
 * <code>getLastFrameDuration</code> returns the duration, in milliseconds, of
 * the most recently completed rendering frame.<P>
 * <code>getFrameNumber</code> returns the frame number for this view.<P>
 * <code>getMaxFrameStartTimes</code> retrieves the implementation-dependent
 * maximum number of frames whose start times will be recorded by
 * the system.<P>
 * <code>getFrameStartTimes</code> copies the last k frame start time values
 * into the user-specified array.<P>
 * </UL>
 * <b>View Traversal and Behavior Scheduling</b><P>
 * The following methods control the traversal, the rendering, and
 * the execution of the behavior scheduler for this view:<P>
 * <UL>
 * <code>startBehaviorScheduler</code> starts the behavior scheduler 
 * running after it has been stopped.<P>
 * <code>stopBehaviorScheduler</code> stops the behavior scheduler after all
 * currently-scheduled behaviors are executed.<P>
 * <code>isBehaviorSchedulerRunning</code> retrieves a flag that indicates
 * whether the behavior scheduler is currently running.<P>
 * <code>startView</code> starts traversing this view and starts the renderers
 * associated with all canvases attached to this view.<P>
 * <code>stopView</code> stops traversing this view after the current state of
 * the scene graph is reflected on all canvases attached to this
 * view.<P>
 * <code>isViewRunning</code> returns a flag indicating whether the traverser
 * is currently running on this view.<P>
 * </UL>
 * Note: The above six methods are heavy-weight methods intended
 * for verification and image capture (recording). They are not
 * intended to be used for flow control.<P>
 *
 * <b>Scene Antialiasing</b><P>
 *
 * The following methods set and retrieve the scene antialiasing
 * flag. Scene antialiasing is either enabled or disabled for this
 * view. If enabled, the entire scene will be antialiased on each
 * canvas in which scene antialiasing is available. Scene
 * antialiasing is disabled by default.<P>
 * <UL>
 * <code>setSceneAntialiasingEnable</code> sets the scene antialiasing flag.<P>
 * <code>getSceneAntialiasingEnable</code> returns the scene antialiasing
 * flag.<P>
 * </UL>
 * Note that line and point antialiasing are independent of scene 
 * antialiasing. If antialiasing is enabled for lines and points, 
 * the lines and points will be antialiased prior to scene antialiasing.
 * If scene antialiasing is disabled, antialiased lines and points will
 * still be antialiased.
 * <p>
 * <b>Note:</b> Scene antialiasing is ignored in pure immediate mode,
 * but is supported in mixed-immediate mode.
 * <p>
 * <b>Depth Buffer</b><P>
 *
 * The following two methods enable and disable automatic freezing
 * of the depth buffer for objects rendered during the transparent
 * rendering pass (that is, objects rendered using alpha blending)
 * for this view. If enabled, depth buffer writes are disabled
 * during the transparent rendering pass regardless of the value
 * of the depth-buffer-write-enable flag in the RenderingAttributes
 * object for a particular node. This flag is enabled by default.<P>
 * <UL>
 * <code>setDepthBufferFreezeTransparent</code> enables depth buffer freezing.<P>
 * <code>getDepthBufferFreezeTransparent</code> retrieves the depth buffer
 * flag.<P>
 * </UL>
 * Transparent objects include BLENDED transparent primitives
 * and antialiased lines
 * and points. Transparent objects do not include opaque objects
 * or primitives rendered with SCREEN_DOOR transparency.<p>
 *
 * <b>Sensors</b><P>
 *
 * The following methods retrieve the sensor's location in the
 * virtual world:<P>
 * <UL>
 * <code>getSensorToVworld</code> takes the sensor's last reading and
 * generates a sensor-to-vworld coordinate system transform. This
 * Transform3D object takes points in that sensor's local coordinate
 * system and transforms them into virtual world coordinates.<P>
 *
 * <code>getSensorHotSpotInVworld</code> retrieves the specified sensor's
 * last hotspot location in virtual world coordinates.<P>
 * </UL>
 *
 * <b>Compatibility Mode</b><P>
 *
 * A camera-based view model allows application programmers to think
 * about the images displayed on the computer screen as if a virtual
 * camera took those images. Such a view model allows application
 * programmers to position and orient a virtual camera within a
 * virtual scene, to manipulate some parameters of the virtual
 * camera's lens (specify its field of view), and to specify the
 * locations of the near and far clipping planes.<P>
 * Java 3D allows applications to enable compatibility mode for
 * room-mounted, non-head-tracked display environments, or to disable
 * compatibility mode using the following methods. Camera-based
 * viewing functions are only available in compatibility mode.<P>
 * <UL>
 * <code>setCompatibilityModeEnable</code> turns compatibility mode on or off.
 * Compatibility mode is disabled by default.<P>
 * <code>getCompatabilityModeEnable</code> returns the compatibility mode
 * enable flag.<P>
 * </UL>
 * Use of these view-compatibility functions will disable some of
 * Java 3D's view model features and limit the portability of Java
 * 3D programs. These methods are primarily intended to help
 * jump-start porting of existing applications.<P>
 *
 * Setting the Viewing Transform<P>
 *
 * The View object provides the following compatibility-mode
 * methods that operate on the viewing transform.<P>
 * <UL>
 * <code>setVpcToEc</code> a compatibility mode method that 
 * specifies the ViewPlatform
 * coordinates (VPC) to eye coordinates viewing transform.<P>
 * <code>getVpcToEc</code> returns the VPC.<P>
 * </UL>
 * Setting the Projection Transform
 * <p>
 * The View object provides the following compatibility-mode
 * methods that operate on the projection transform:<P>
 * <UL>
 * The <code>setLeftProjection</code> and <code>setRightProjection</code> 
 * methods specify
 * a viewing frustum for the left and right eye that transforms
 * points in eye coordinates to clipping coordinates.<P>
 * 
 * The <code>getLeftProjection</code> and <code>getRightProjection</code> 
 * methods return
 * the viewing frustum for the left and right eye.<P>
 * </UL>
 *
 * <p>
 * <b>Additional Information</b>
 * <p>
 * For more information, see the
 * <a href="doc-files/intro.html">Introduction to the Java 3D API</a> and
 * <a href="doc-files/ViewModel.html">View Model</a>
 * documents.
 *
 * @see Canvas3D
 * @see PhysicalBody
 * @see PhysicalEnvironment
 * @see ViewPlatform
 * @see TransparencyAttributes
 */

public class View extends Object {
    /**
     * Specifies a policy whereby the origin of physical or virtual
     * coordinates is relative to the position of the nominal head.
     * When used as a view attach policy, this sets the origin of view
     * platform coordinates to be at the eyepoint.
     * @see ViewPlatform#setViewAttachPolicy
     * @see PhysicalEnvironment#setCoexistenceCenterInPworldPolicy
     */
    public static final int NOMINAL_HEAD = 0;

    /**
     * Specifies a policy whereby the origin of physical or virtual
     * coordinates is relative to the position of the nominal feet.
     * When used as a view attach policy, this sets the origin of view
     * platform coordinates to be at the ground plane.
     * @see ViewPlatform#setViewAttachPolicy
     * @see PhysicalEnvironment#setCoexistenceCenterInPworldPolicy
     */
    public static final int NOMINAL_FEET = 1;

    /**
     * Specifies a policy whereby the origin of physical or virtual
     * coordinates is relative to the screen.
     * When used as a view attach policy, this sets the origin of view
     * platform coordinates to be at the center of the window or screen,
     * in effect, allowing the user to view objects from an optimal viewpoint.
     * @see ViewPlatform#setViewAttachPolicy
     * @see PhysicalEnvironment#setCoexistenceCenterInPworldPolicy
     */
    public static final int NOMINAL_SCREEN = 2;

    /**
     * Specifies that the screen scale for this view is derived from
     * the physical screen size.  This scale factor is computed as follows:
     * <ul>
     * physical_screen_width / 2.0
     * </ul>
     * This allows an application to define a world in a normalized
     * [-1,1] space and view it on a screen of any size.
     * @see #setScreenScalePolicy
     */
    public static final int SCALE_SCREEN_SIZE = 0;

    /**
     * Specifies that the screen scale for this view is taken directly
     * from the user-provided screenScale parameter.
     * @see #setScreenScalePolicy
     * @see #setScreenScale
     */
    public static final int SCALE_EXPLICIT = 1;

    /**
     * Specifies that the associated distance is measured
     * from the screen in virtual world coordinates.
     * Policy for interpreting clip plane distances.
     * Used in specifying the policy in frontClipPolicy and backClipPolicy.
     * @see #setFrontClipPolicy
     * @see #setBackClipPolicy
     */
    public static final int VIRTUAL_SCREEN = 0;

    /**
     * Specifies that the associated distance is measured
     * from the screen in meters.
     * Policy for interpreting clip plane distances.
     * Used in specifying the policy in frontClipPolicy and backClipPolicy.
     * @see #setFrontClipPolicy
     * @see #setBackClipPolicy
     */
    public static final int PHYSICAL_SCREEN = 1;

    /**
     * Specifies that the associated distance is measured
     * from the eye in virtual world coordinates.
     * Policy for interpreting clip plane distances.
     * Used in specifying the policy in frontClipPolicy and backClipPolicy.
     * @see #setFrontClipPolicy
     * @see #setBackClipPolicy
     */
    public static final int VIRTUAL_EYE = 2;

    /**
     * Specifies that the associated distance is measured
     * from the eye in meters.
     * Policy for interpreting clip plane distances.
     * Used in specifying the policy in frontClipPolicy and backClipPolicy.
     * This is the default policy for both front and back clipping.
     * @see #setFrontClipPolicy
     * @see #setBackClipPolicy
     */
    public static final int PHYSICAL_EYE = 3;

    /**
     * Policy for resizing and moving windows.
     * Used in specifying windowResizePolicy and windowMovementPolicy.
     * VIRTUAL_WORLD specifies that the associated action takes place
     * in the virtual world as well as in the physical world.
     * @see #setWindowResizePolicy
     * @see #setWindowMovementPolicy
     */
    public static final int VIRTUAL_WORLD = 0;

    /**
     * Policy for resizing and moving windows.
     * Used in specifying windowResizePolicy and windowMovementPolicy.
     * PHYSICAL_WORLD specifies that the specified action takes place
     * only in the physical world.
     * @see #setWindowResizePolicy
     * @see #setWindowMovementPolicy
     */
    public static final int PHYSICAL_WORLD = 1;

    /**
     * Policy for placing the eyepoint in non-head-tracked modes.
     * Specifies that Java 3D should interpret the
     * given fixed eyepoint position as relative to the entire screen.
     * This implies
     * that the view frustum shape will change whenever a
     * user moves the location of a window on the screen.
     * @see #setWindowEyepointPolicy
     */
    public static final int RELATIVE_TO_SCREEN = 0;

    /**
     * Policy for placing the eyepoint in non-head-tracked modes.
     * Specifies that Java 3D should interpret the
     * given fixed-eyepoint position as relative to the window.
     * @see #setWindowEyepointPolicy
     */
    public static final int RELATIVE_TO_WINDOW = 1;

    /**
     * Policy for placing the eyepoint in non-head-tracked modes.
     * Specifies that Java 3D should
     * modify the position of the eyepoint to match any changes in field
     * of view; the view frustum will change whenever the application
     * program changes the field of view.
     * <p>
     * NOTE: when this policy is specified, the Z coordinate of
     * the derived eyepoint is used in place of
     * nominalEyeOffsetFromNominalScreen.
     * @see #setWindowEyepointPolicy
     */
    public static final int RELATIVE_TO_FIELD_OF_VIEW = 2;

    /**
     * Policy for placing the eyepoint in non-head-tracked modes.
     * Specifies that Java 3D should interpret the fixed eyepoint
     * position in the view as relative to the origin
     * of coexistence coordinates.  This eyepoint is transformed from
     * coexistence coordinates to image plate coordinates for each
     * Canvas3D.
     * As in RELATIVE_TO_SCREEN mode, this implies
     * that the view frustum shape will change whenever a
     * user moves the location of a window on the screen.
     * @see #setWindowEyepointPolicy
     *
     * @since Java 3D 1.2
     */
    public static final int RELATIVE_TO_COEXISTENCE = 3;

    /**
     * Specifies that monoscopic view generated should be the view as seen
     * from the left eye.
     * @see Canvas3D#setMonoscopicViewPolicy
     */
    public static final int LEFT_EYE_VIEW = 0;

    /**
     * Specifies that monoscopic view generated should be the view as seen
     * from the right eye.
     * @see Canvas3D#setMonoscopicViewPolicy
     */
    public static final int RIGHT_EYE_VIEW = 1;

    /**
     * Specifies that monoscopic view generated should be the view as seen
     * from the 'center eye', the fictional eye half-way between the left and
     * right eye.
     * @see Canvas3D#setMonoscopicViewPolicy
     */
    public static final int CYCLOPEAN_EYE_VIEW = 2;

    /**
     * Specifies that the viewing environment for this view is a
     * standard screen-based display environment.
     * In this mode, Java 3D will compute new viewpoints
     * using that sequence of transforms appropriate to screen-based,
     * display environments, that may or may not include head tracking
     * (e.g., a monoscopic screen, fish-tank VR, portals, VR-desks).
     * This is the default mode.
     * @see #setViewPolicy
     */
    public static final int SCREEN_VIEW = 0;

    /**
     * Specifies that the viewing environment for this view is a
     * head-mounted display environment.
     * In this mode, Java 3D will compute new viewpoints
     * using that sequence of transforms appropriate to head-mounted display
     * environments.  These environments are generally head-tracked.
     * @see #setViewPolicy
     */
    public static final int HMD_VIEW = 1;

    /**
     * Specifies that Java 3D should generate a parallel projection matrix
     * for this View.
     * @see #setProjectionPolicy
     */
    public static final int PARALLEL_PROJECTION = 0;

    /**
     * Specifies that Java 3D should generate a perspective projection matrix
     * for this View.
     * This is the default mode.
     * @see #setProjectionPolicy
     */
    public static final int PERSPECTIVE_PROJECTION = 1;

    /**
     * Policy that specifies that only visible objects should be drawn.
     * This is the default mode.
     * @see #setVisibilityPolicy
     *
     * @since Java 3D 1.2
     */
    public static final int VISIBILITY_DRAW_VISIBLE = 0;

    /**
     * Policy that specifies that only invisible objects should be drawn.
     * @see #setVisibilityPolicy
     *
     * @since Java 3D 1.2
     */
    public static final int VISIBILITY_DRAW_INVISIBLE = 1;

    /**
     * Policy that specifies that both visible and invisible objects
     * should be drawn.
     * @see #setVisibilityPolicy
     *
     * @since Java 3D 1.2
     */
    public static final int VISIBILITY_DRAW_ALL = 2;

    /**
     * Policy that specifies that no sorting of transparent objects
     * is done.
     * This is the default mode.
     * @see #setTransparencySortingPolicy
     *
     * @since Java 3D 1.3
     */
    public static final int TRANSPARENCY_SORT_NONE = 0;

    /**
     * Policy that specifies that transparent objects
     * are sorted from back to front on a per-geometry basis.
     * @see #setTransparencySortingPolicy
     *
     * @since Java 3D 1.3
     */
    public static final int TRANSPARENCY_SORT_GEOMETRY = 1;


    //
    // The AWT window for display.
    //
    // This object can be queried to obtain:
    // screen width in pixels
    // screen height in pixels
    // window width in pixels
    // window height in pixels
    // window upper left corner location in pixels relative to screen
    //
    // Use getCanvases() to access this
    private Vector canvases = new Vector(3);

    //
    // The current universe associated with this view
    //
    VirtualUniverse universe = null;

    //
    // The RenderBin associated with this view.
    //
    RenderBin renderBin = null;

    // This is the SoundScheduler associated with this view.
    SoundScheduler soundScheduler = null;

    // This is the thread associated with this view.
    SoundRenderer soundRenderer = new SoundRenderer();

    // AudioDevice enumerator current position
    //    AudioDeviceEnumerator allAudioEnumerator = null;
    
    // These are used for tracking the frame times
    static final int NUMBER_FRAME_START_TIMES = 10;

    long[] frameStartTimes = new long[NUMBER_FRAME_START_TIMES];
    long[] frameNumbers = new long[NUMBER_FRAME_START_TIMES];
    int currentFrameIndex = 0;

    // These are the values that are set at the end of each frame
    long currentFrameStartTime = 0;
    long currentFrameDuration = 0;
    long currentFrameNumber = 0;

    // These are the ones that get updated directly by MC
    long frameNumber = 0;
    long startTime = 0;
    long stopTime = 0;    

    // Support dynamic video resize -- DVR.
    Viewer viewer = null; // Cached the associate viewer of this view.
    boolean firstTime = true;
//    float dvrFactor = 1.0f;
//    boolean dvrResizeCompensation = true;

    // User adjustable minimum frame cycle time
    long minFrameCycleTime;

    // True when stopBehaviorScheduler invoke
    boolean stopBehavior;

    //
    // View cache for this view.
    //
    ViewCache viewCache = null;
    
    // Compatibility mode related field has changed.
    // { compatibilityModeEnable, compatVpcToEc, compatLeftProjection,
    //   compatRightProjection }
    static final int COMPATIBILITY_MODE_DIRTY         = 0x01;
    // ScreenScalePolicy field has changed.
    static final int SCREEN_SCALE_POLICY_DIRTY        = 0x02;
    // Screen scale field has changed.
    static final int SCREEN_SCALE_DIRTY               = 0x04;
    // Window Resize Policy field has changed.
    static final int WINDOW_RESIZE_POLICY_DIRTY       = 0x08;
    // View Policy eye in image plate field has changed.
    static final int VIEW_POLICY_DIRTY                = 0x10;
    // Clip related field has changed.
    // { frontClipDistance, backClipDistance, frontClipPolicy, backClipPolicy }
    static final int CLIP_DIRTY                       = 0x20;
    // Projection Policy field has changed.
    static final int PROJECTION_POLICY_DIRTY          = 0x40;
    // Window Movement Policy field has changed.
    static final int WINDOW_MOVEMENT_POLICY_DIRTY     = 0x80;
    // Window Eye Point Policy field has changed.
    static final int WINDOW_EYE_POINT_POLICY_DIRTY    = 0x100;
    // Monoscopic View Policy field has changed.
    static final int MONOSCOPIC_VIEW_POLICY_DIRTY     = 0x200;
    // Field Of View field has changed.
    static final int FIELD_OF_VIEW_DIRTY              = 0x400; 
    // Tracking Enable field has changed.
    static final int TRACKING_ENABLE_DIRTY            = 0x800;
    // User Head To Vworld Enable field has changed.
    static final int USER_HEAD_TO_VWORLD_ENABLE_DIRTY = 0x1000;
    // coexistenceCenteringEnable flag has changed.
    static final int COEXISTENCE_CENTERING_ENABLE_DIRTY = 0x2000;
    // leftManualEyeInCoexistence has changed.
    static final int LEFT_MANUAL_EYE_IN_COEXISTENCE_DIRTY = 0x4000;
    // rightManualEyeInCoexistence has changed.
    static final int RIGHT_MANUAL_EYE_IN_COEXISTENCE_DIRTY = 0x8000;
    // visibilityPolicy has changed.
    static final int VISIBILITY_POLICY_DIRTY               = 0x10000;
    
    // This is not from View object. It is here for the purpose
    // keeping all ViewCache's dirty mask bit declaration in one place.
    // ViewPlatformRetained viewAttach Policy field has changed.
    static final int VPR_VIEW_ATTACH_POLICY_DIRTY = 0x10000;
    static final int VPR_VIEWPLATFORM_DIRTY       = 0x20000;

    // PhysicalEnvironment fields has changed.
    static final int PE_COE_TO_TRACKER_BASE_DIRTY           = 0x100000;
    static final int PE_TRACKING_AVAILABLE_DIRTY            = 0x200000;
    static final int PE_COE_CENTER_IN_PWORLD_POLICY_DIRTY   = 0x400000;
    
    // PhysicalBody fields has changed.
    static final int PB_EYE_POSITION_DIRTY                             = 0x1000000;
    static final int PB_EAR_POSITION_DIRTY                             = 0x2000000;
    static final int PB_NOMINAL_EYE_HEIGHT_FROM_GROUND_DIRTY           = 0x4000000;
    static final int PB_NOMINAL_EYE_OFFSET_FROM_NOMINAL_SCREEN_DIRTY   = 0x8000000;

    
    // Mask that indicates this View's view dependence info. has changed,
    // and CanvasViewCache may need to recompute the final view matries.
    int vDirtyMask = (COMPATIBILITY_MODE_DIRTY | SCREEN_SCALE_POLICY_DIRTY
		      | SCREEN_SCALE_DIRTY | WINDOW_RESIZE_POLICY_DIRTY
		      | VIEW_POLICY_DIRTY | CLIP_DIRTY
		      | PROJECTION_POLICY_DIRTY | WINDOW_MOVEMENT_POLICY_DIRTY
		      | WINDOW_EYE_POINT_POLICY_DIRTY | MONOSCOPIC_VIEW_POLICY_DIRTY
		      | FIELD_OF_VIEW_DIRTY | TRACKING_ENABLE_DIRTY
		      | USER_HEAD_TO_VWORLD_ENABLE_DIRTY
		      | COEXISTENCE_CENTERING_ENABLE_DIRTY
		      | LEFT_MANUAL_EYE_IN_COEXISTENCE_DIRTY
		      | RIGHT_MANUAL_EYE_IN_COEXISTENCE_DIRTY
		      | VISIBILITY_POLICY_DIRTY);    
     

    //
    // This object contains a specification of the user's physical body.
    //
    // Attributes of this object are defined in head coordinates and
    // include information such as the location of the user's eyes and 
    // ears.
    // The origin is defined to be halfway between the left and right eye
    // in the plane of the face.
    // The x-axis extends to the right (of the head looking out from the head).
    // The y-axis extends up. The z-axis extends to the rear of the head.
    //
    PhysicalBody  physicalBody;

    // This object contains a specification of the physical environment.
    PhysicalEnvironment  physicalEnvironment;

    // View model compatibility mode flag
    boolean compatibilityModeEnable = false;
    
    // View model coexistenceCenteringEnable flag
    boolean coexistenceCenteringEnable = true;

    Point3d leftManualEyeInCoexistence = new Point3d();
    Point3d rightManualEyeInCoexistence = new Point3d();

    //
    // Indicates which major mode of view computation to use:
    // HMD mode or screen/fish-tank-VR mode.
    //
    int		viewPolicy = SCREEN_VIEW;

    // The current projection policy (parallel versus perspective)
    int projectionPolicy = PERSPECTIVE_PROJECTION;

    //
    // The view model's field of view.
    //
    double	fieldOfView = 45.0 * Math.PI / 180.0;

    //
    // The distance away from the clip origin
    // in the direction of gaze for the front and back clip planes.
    // The default values are in meters.
    //
    double	frontClipDistance = 0.1;
    double	backClipDistance = 10.0;

    // This variable specifies where the screen scale comes from
    int screenScalePolicy = SCALE_SCREEN_SIZE;

    // The screen scale value used when the screen scale policy is
    // SCALE_EXPLICIT
    double screenScale = 1.0;

    //
    // This variable specifies how Java 3D modifies the view when
    // the window is resized (VIRTUAL_WORLD or PHYSICAL_WORLD).
    //
    int windowResizePolicy = PHYSICAL_WORLD;

    //
    // This variable specifies how Java 3D modifies the view when
    // the window is moved (VIRTUAL_WORLD or PHYSICAL_WORLD).
    //
    int windowMovementPolicy = PHYSICAL_WORLD;

    //
    // Specifies how Java 3D handles the predefined eyepoint in
    // non-head-tracked environment (RELATIVE_TO_SCREEN,
    // RELATIVE_TO_WINDOW, RELATIVE_TO_FIELD_OF_VIEW, or
    // RELATIVE_TO_COEXISTENCE)
    //
    int windowEyepointPolicy = RELATIVE_TO_FIELD_OF_VIEW;

    //
    // Specifies how Java 3D generates monoscopic view
    // (LEFT_EYE_VIEW, RIGHT_EYE_VIEW, or CYCLOPEAN_EYE_VIEW).
    //
    int monoscopicViewPolicy = CYCLOPEAN_EYE_VIEW;

    /**
     * Defines the policy for placing the front clipping plane.
     * Legal values include PHYSICAL_EYE, PHYSICAL_SCREEN,
     * VIRTUAL_EYE, and VIRTUAL_SCREEN.
     */
    int		frontClipPolicy = PHYSICAL_EYE;

    /**
     * Defines the policy for placing the back clipping plane.
     */
    int		backClipPolicy = PHYSICAL_EYE;

    /**
     * Defines the visibility policy.
     */
    int		visibilityPolicy = VISIBILITY_DRAW_VISIBLE;

    /**
     * Defines the transparency sorting policy.
     */
    int		transparencySortingPolicy = TRANSPARENCY_SORT_NONE;

    /**
     * Flag to enable tracking, if so allowed by the trackingAvailable flag.
     */
    boolean		trackingEnable = false;

    /**
     * This setting enables the continuous updating by Java 3D of the
     * userHeadToVworld transform.
     */
    boolean userHeadToVworldEnable = false;

    /**
     * The view platform currently associated with this view.
     */
    private ViewPlatform viewPlatform = null;

    // The current compatibility mode view transform
    Transform3D compatVpcToEc = new Transform3D();

    // The current compatibility mode projection transforms
    Transform3D compatLeftProjection = new Transform3D();
    Transform3D compatRightProjection = new Transform3D();

    // The long id of this view - used for dirty bit evaluation in the scene graph
    Integer viewId = null;
    int viewIndex = -1;

    // A boolean that indicates whether or not this is the primary view
    boolean primaryView = false;

    // A boolean that indicates whether or not this view is active as
    // seen by MasterControl
    boolean active = false;

    // A boolean that indicates whether or not this view is active as
    // seen by this view. There is a delay before MasterControl set
    // active flag, so a local activeStatus is used. Otherwise
    // activate event may lost if it proceed by deactivate event
    // but MC not yet set active to false. This happens in
    // viewplatform detach and attach.
    boolean activeStatus = false;

    // This boolean indicates whether or not the view is running.  It
    // is used for startView/stopView
    volatile boolean isRunning = true;

    // A flag to indicate that we are in a canvas callback routine
    boolean inCanvasCallback = false;

    //
    // Flag to enable depth buffer freeze during trasparent rendering pass
    //  
    boolean depthBufferFreezeTransparent = true;

    //
    // Flag to enable scene antialiasing
    //  
    boolean sceneAntialiasingEnable = false;

    //
    // Flag to enable local eye lighting
    //  
    boolean localEyeLightingEnable = false;

    // Array Lists to track the screens and canvases associated with this View.
    // use getScreens() to access this
    private ArrayList screenList = new ArrayList();

    // use getCanvasList() to access this
    private ArrayList canvasList = new ArrayList();

    private Canvas3D[][] cachedCanvasList;
    private Canvas3D[] cachedCanvases;
    private Screen3D[] cachedScreens;
    private int longestScreenList = 0;
    private boolean canvasesDirty = true;

    // Flag to notify user thread when renderOnce is finished
    volatile boolean renderOnceFinish = true;
    
    // Lock to synchronize start/stop/renderOnce call
    private Object startStopViewLock = new Object();

    // Lock for evaluateActive() only. This is used to prevent
    // using lock this which will cause deadlock when MC call
    // snapshot which waitForMC() in user thread.
    private Object evaluateLock = new Object();

    /**
     * use for stop view, when stopview, set to count -1,
     * when reach 1, call stopView() in MC and reset to -1.
     */
    int stopViewCount = -1; 

    /**
     * False if current frame cycle time less than minimum frame cycle time 
     */
    boolean isMinCycleTimeAchieve = true;

    // Time to sleep if minimum frame cycle time not achieve
    long sleepTime = 0;

    // use in pure immediate mode to tell whether this view rendering
    // thread is added in MC renderThreadData
    volatile boolean inRenderThreadData = false;

    // use to notify MC that render bin has run once, and is ready for
    // renderer to render
    boolean renderBinReady = false;

    // No of time setUniverse() is invoke
    long universeCount = 0;

    // The universe count when UNREGISTER_VIEW request is post,
    // this is used to distingish whether new setUniverse() is
    // invoked after UNREGISTER_VIEW request post to avoid
    // resetting the newly set universe.
    long resetUnivCount = 0;

    // This notify user thread waitForMC() to continue when 
    // MC finish unregisterView
    volatile boolean doneUnregister = false;

    static final int TRANSP_SORT_POLICY_CHANGED           = 0x0001;
    static final int OTHER_ATTRS_CHANGED                  = 0x0002;

    /**
     * Constructs a View object with default parameters.  The default
     * values are as follows:
     * <ul>
     * view policy : SCREEN_VIEW<br>
     * projection policy : PERSPECTIVE_PROJECTION<br>
     * screen scale policy : SCALE_SCREEN_SIZE<br>
     * window resize policy : PHYSICAL_WORLD<br>
     * window movement policy : PHYSICAL_WORLD<br>
     * window eyepoint policy : RELATIVE_TO_FIELD_OF_VIEW<br>
     * monoscopic view policy : CYCLOPEAN_EYE_VIEW<br>
     * front clip policy : PHYSICAL_EYE<br>
     * back clip policy : PHYSICAL_EYE<br>
     * visibility policy : VISIBILITY_DRAW_VISIBLE<br>
     * transparency sorting policy : TRANSPARENCY_SORT_NONE<br>
     * coexistenceCentering flag : true<br>
     * compatibility mode : false<br>
     * left projection : identity<br>
     * right projection : identity<br>
     * vpc to ec transform : identity<br>
     * physical body : null<br>
     * physical environment : null<br>
     * screen scale : 1.0<br>
     * field of view : PI/4<br>
     * left manual eye in coexistence : (-0.033, 0.0, 0.4572)<br>
     * right manual eye in coexistence : (0.033, 0.0, 0.4572)<br>
     * front clip distance : 0.1<br>
     * back clip distance : 10.0<br>
     * tracking enable : false<br>
     * user head to vworld enable : false<br>
     * list of Canvas3D objects : empty<br>
     * depth buffer freeze transparent : true<br>
     * scene antialiasing : false<br>
     * local eye lighting : false<br>
     * view platform : null<br>
     * behavior scheduler running : true<br>
     * view running : true<br>
     * minimum frame cycle time : 0<br>
     * </ul>
     */
    public View() {
        viewCache = new ViewCache(this);
    }

    /**
     * Sets the policy for view computation.
     * This variable specifies how Java 3D uses its transforms in
     * computing new viewpoints.
     * <UL>
     * <LI>SCREEN_VIEW specifies that Java 3D should compute a new viewpoint
     * using the sequence of transforms appropriate to screen-based
     * head-tracked display environments (fish-tank VR/portals/VR-desks).
     * </LI>
     * <LI>HMD_VIEW specifies that Java 3D should compute a new viewpoint
     * using the sequence of transforms appropriate to head mounted
     * display environments.
     * </LI>
     * </UL>
     * The default view policy is SCREEN_VIEW.
     * @param policy the new policy, one of SCREEN_VIEW or HMD_VIEW
     * @exception IllegalArgumentException if policy is a value other than
     * SCREEN_VIEW or HMD_VIEW
     * @exception IllegalStateException if the specified policy
     * is HMD_VIEW and if any canvas associated with this view is
     * a stereo canvas with a monoscopicEyePolicy of CYCLOPEAN_EYE_VIEW
     */
    public void setViewPolicy(int policy) {
	if (policy != HMD_VIEW &&
	    policy != SCREEN_VIEW) {
	    
	    throw new IllegalArgumentException(J3dI18N.getString("View0"));
	}
	if(policy == HMD_VIEW) {
	    // Check the following :
	    // 1) If the view is in HMD mode and there exists a canvas in
	    // CYCLOPEAN_EYE_VIEW mode then throw exception.
	    synchronized (canvasList) {
		for (int i=canvases.size()-1; i>=0; i--) {
		    Canvas3D c3d = (Canvas3D)canvases.elementAt(i);
		    
		    if ((c3d.monoscopicViewPolicy == View.CYCLOPEAN_EYE_VIEW) &&
			(!c3d.useStereo)){
			throw new
			    IllegalStateException(J3dI18N.getString("View31"));
		    }
		}
	    }
	}
	synchronized(this) {
	    this.viewPolicy = policy;
	    vDirtyMask |= View.VIEW_POLICY_DIRTY;
	}
	repaint();
    }

    /**
     * Retrieves the current view computation policy for this View.
     * @return one of: SCREEN_VIEW or HMD_VIEW.
     */
    public int getViewPolicy() {
	return this.viewPolicy;
    }
  
    /**
     * Sets the projection policy for this View.
     * This variable specifies the type of projection transform that
     * will be generated.  A value of PARALLEL_PROJECTION specifies that
     * a parallel projection transform is generated.  A value of
     * PERSPECTIVE_PROJECTION specifies that
     * a perspective projection transform is generated.
     * The default projection policy is PERSPECTIVE.
     * @param policy the new policy, one of PARALLEL_PROJECTION or
     * PERSPECTIVE_PROJECTION
     * @exception IllegalArgumentException if policy is a value other than
     * PARALLEL_PROJECTION or PERSPECTIVE_PROJECTION
     */
    public void setProjectionPolicy(int policy) {
	if (policy != PERSPECTIVE_PROJECTION &&
	    policy != PARALLEL_PROJECTION) {

	    throw new IllegalArgumentException(J3dI18N.getString("View1"));
	}
	synchronized(this) {
	    this.projectionPolicy = policy;
	    vDirtyMask |= View.PROJECTION_POLICY_DIRTY;
	}

	repaint();
    }

    /**
     * Retrieves the current projection policy for this View.
     * @return one of: PARALLEL_PROJECTION or PERSPECTIVE_PROJECTION.
     */
    public int getProjectionPolicy() {
	return this.projectionPolicy;
    }
  
    /**
     * Sets the screen scale policy for this view.
     * This policy specifies how the screen scale is derived.
     * The value is either SCALE_SCREEN_SIZE or SCALE_EXPLICIT.
     * A value of SCALE_SCREEN_SIZE specifies that the scale is derived
     * from the size of the physical screen.  A value of SCALE_EXPLICIT
     * specifies that the scale is taken directly from the screenScale
     * parameter.
     * The default screen scale policy is SCALE_SCREEN_SIZE.
     * @param policy the new policy, one of SCALE_SCREEN_SIZE or
     * SCALE_EXPLICIT.
     */
    public void setScreenScalePolicy(int policy) {

	synchronized(this) {
	    this.screenScalePolicy = policy;
	    vDirtyMask |= View.SCREEN_SCALE_POLICY_DIRTY;
	}

	repaint();
    }

    /**
     * Returns the current screen scale policy, one of:
     * SCALE_SCREEN_SIZE or SCALE_EXPLICIT.
     * @return the current screen scale policy
     */
    public int getScreenScalePolicy() {
	return this.screenScalePolicy;
    }

    /**
     * Sets the window resize policy.
     * This variable specifies how Java 3D modifies the view when
     * users resize windows. The variable can contain one of
     * VIRTUAL_WORLD or PHYSICAL_WORLD.
     * A value of VIRTUAL_WORLD implies that the original image
     * remains the same size on the screen but the user sees more
     * or less of the virtual world depending on whether the window
     * grew or shrank in size.
     * A value of PHYSICAL_WORLD implies that the original image
     * continues to fill the window in the same way using more or
     * less pixels depending on whether the window grew or shrank
     * in size.
     * The default window resize policy is PHYSICAL_WORLD.
     * @param policy the new policy, one of VIRTUAL_WORLD or PHYSICAL_WORLD
     */
    public void setWindowResizePolicy(int policy) {

	synchronized(this) {
	    this.windowResizePolicy = policy;
	    vDirtyMask |= View.WINDOW_RESIZE_POLICY_DIRTY;
	}
	repaint();
    }

    /**
     * Returns the current window resize policy, one of:
     * VIRTUAL_WORLD or PHYSICAL_WORLD.
     * @return the current window resize policy
     */
    public int getWindowResizePolicy() {
	return this.windowResizePolicy;
    }

    /**
     * Sets the window movement policy.
     * This variable specifies what part of the virtual world Java 3D
     * draws as a function of window placement on the screen. The
     * variable can contain one of VIRTUAL_WORLD or PHYSICAL_WORLD.
     * A value of VIRTUAL_WORLD implies that the image seen in the
     * window changes as the position of the window shifts on the
     * screen.  (This mode acts as if the window were a window into
     * the virtual world.)
     * A value of PHYSICAL_WORLD implies that the image seen in the
     * window remains the same no matter where the user positions
     * the window on the screen.
     * The default window movement policy is PHYSICAL_WORLD.
     * @param policy the new policy, one of VIRTUAL_WORLD or PHYSICAL_WORLD
     */
    public void setWindowMovementPolicy(int policy) {

	synchronized(this) {
	    this.windowMovementPolicy = policy;
	    vDirtyMask |= View.WINDOW_MOVEMENT_POLICY_DIRTY;
	}
	repaint();
    }

    /**
     * Returns the current window movement policy,
     * one of: VIRTUAL_WORLD or PHYSICAL_WORLD.
     * @return the current window movement policy
     */
    public int getWindowMovementPolicy() {
	return this.windowMovementPolicy;
    }

    /**
     * Sets the view model's window eyepoint policy.
     * This variable specifies how Java 3D handles the predefined eye
     * point in a non-head-tracked environment.  The variable can contain
     * one of:
     * <UL>
     * <LI>RELATIVE_TO_SCREEN, Java 3D should interpret the
     * given fixed-eyepoint position as relative to the screen (this
     * implies that the view frustum shape will change whenever a
     * user moves the location of a window on the screen).
     * </LI>
     * <LI>RELATIVE_TO_WINDOW, Java 3D should interpret the
     * given fixed-eyepoint position as relative to the window.  In this
     * mode, the X and Y values are taken as the center of the window and
     * the Z value is taken from the canvas eyepoint position.
     * </LI>
     * <LI>RELATIVE_TO_FIELD_OF_VIEW, Java 3D should
     * modify the position of the eyepoint to match any changes in field
     * of view (the view frustum will change whenever the application
     * program changes the field of view).
     * </LI>
     * <LI>RELATIVE_TO_COEXISTENCE, Java 3D should interpret the eye's
     * position in coexistence coordinates. In this mode, the eye position
     * is taken from the view (rather than the Canvas3D) and transformed from
     * coexistence coordinates to image plate coordinates for each
     * Canvas3D.  The resulting eye position is relative to the screen (this
     * implies that the view frustum shape will change whenever a
     * user moves the location of a window on the screen).
     * </LI>
     * </UL>
     * The default window eyepoint policy is RELATIVE_TO_FIELD_OF_VIEW.
     * @param policy the new policy, one of RELATIVE_TO_SCREEN,
     * RELATIVE_TO_WINDOW, RELATIVE_TO_FIELD_OF_VIEW, or
     * RELATIVE_TO_COEXISTENCE
     */
    public void setWindowEyepointPolicy(int policy) {
	synchronized(this) {
	    this.windowEyepointPolicy = policy;
	    vDirtyMask |= View.WINDOW_EYE_POINT_POLICY_DIRTY;
	}

	repaint();
    }

    /**
     * Returns the current window eyepoint policy, one of:
     * RELATIVE_TO_SCREEN, RELATIVE_TO_WINDOW, RELATIVE_TO_FIELD_OF_VIEW or
     * RELATIVE_TO_COEXISTENCE.
     * @return the current window eyepoint policy
     */
    public int getWindowEyepointPolicy() {
	return this.windowEyepointPolicy;
    }

    /**
     * @deprecated As of Java 3D version 1.2, replaced by
     * <code>Canvas3D.setMonoscopicViewPolicy</code>
     */
    public void setMonoscopicViewPolicy(int policy) {
	synchronized(this) {
	    this.monoscopicViewPolicy = policy;
	    vDirtyMask |= View.MONOSCOPIC_VIEW_POLICY_DIRTY;
	}
	repaint();
    }

    /**
     * @deprecated As of Java 3D version 1.2, replaced by
     * <code>Canvas3D.getMonoscopicViewPolicy</code>
     */
    public int getMonoscopicViewPolicy() {
	return this.monoscopicViewPolicy;
    }

    /**
     * Sets the coexistenceCentering enable flag to true or false.
     * If the coexistenceCentering flag is true, the center of
     * coexistence in image plate coordinates, as specified by the
     * trackerBaseToImagePlate transform, is translated to the center
     * of either the window or the screen in image plate coordinates,
     * according to the value of windowMovementPolicy.
     *
     * <p>
     * By default, coexistenceCentering is enabled.  It should be
     * disabled if the trackerBaseToImagePlate calibration transform
     * is set to a value other than the identity (for example, when
     * rendering to multiple screens or when head tracking is
     * enabled).  This flag is ignored for HMD mode, or when the
     * coexistenceCenterInPworldPolicy is <i>not</i>
     * NOMINAL_SCREEN.
     *
     * @param flag the new coexistenceCentering enable flag
     *
     * @since Java 3D 1.2
     */
    public void setCoexistenceCenteringEnable(boolean flag) {
	synchronized(this) {
	    this.coexistenceCenteringEnable = flag;
	    vDirtyMask |= View.COEXISTENCE_CENTERING_ENABLE_DIRTY;
	}
	repaint();
    }
    
    /**
     * Retrieves the coexistenceCentering enable flag.
     *
     * @return the current coexistenceCentering enable flag
     *
     * @since Java 3D 1.2
     */
    public boolean getCoexistenceCenteringEnable() {
	return this.coexistenceCenteringEnable;
    }

    /**
     * Sets the compatibility mode enable flag to true or false.
     * Compatibility mode is disabled by default.
     * @param flag the new compatibility mode enable flag
     */
    public void setCompatibilityModeEnable(boolean flag) {
	synchronized(this) {
	    this.compatibilityModeEnable = flag;
	    vDirtyMask |= View.COMPATIBILITY_MODE_DIRTY;
	}
	repaint();
    }
  
    /**
     * Retrieves the compatibility mode enable flag.
     * @return the current compatibility mode enable flag
     */
    public boolean getCompatibilityModeEnable() {
	return this.compatibilityModeEnable;
    }
  
    /**
     * Compatibility mode method that specifies a viewing frustum for
     * the left eye that transforms points in Eye Coordinates (EC) to
     * Clipping Coordinates (CC).
     * If compatibility mode is disabled, then this transform is not used;
     * the actual projection is derived from other values.
     * In monoscopic mode, only the left eye projection matrix is used.
     * @param projection the new left eye projection transform
     * @exception RestrictedAccessException if compatibility mode is disabled.
     */
    public void setLeftProjection(Transform3D projection) {
	if (!compatibilityModeEnable) {
	    throw new RestrictedAccessException(J3dI18N.getString("View2"));
	}

	synchronized(this) {
	    compatLeftProjection.setWithLock(projection);
	    vDirtyMask |= View.COMPATIBILITY_MODE_DIRTY;
	}
	repaint();
    }

    /**
     * Compatibility mode method that specifies a viewing frustum for
     * the right eye that transforms points in Eye Coordinates (EC) to
     * Clipping Coordinates (CC).
     * If compatibility mode is disabled, then this transform is not used;
     * the actual projection is derived from other values.
     * In monoscopic mode, the right eye projection matrix is ignored.
     * @param projection the new right eye projection transform
     * @exception RestrictedAccessException if compatibility mode is disabled.
     */
    public void setRightProjection(Transform3D projection) {
	if (!compatibilityModeEnable) {
	    throw new RestrictedAccessException(J3dI18N.getString("View2"));
	}

	synchronized(this) {
	    compatRightProjection.setWithLock(projection);
	    vDirtyMask |= View.COMPATIBILITY_MODE_DIRTY;
	}

	repaint();
    }

    /**
     * Compatibility mode method that retrieves the current
     * compatibility mode projection transform for the left eye and
     * places it into the specified object.
     * @param projection the Transform3D object that will receive the
     * projection
     * @exception RestrictedAccessException if compatibility mode is disabled.
     */
    public void getLeftProjection(Transform3D projection) {
	if (!compatibilityModeEnable) {
	    throw new RestrictedAccessException(J3dI18N.getString("View4"));
	}

	projection.set(compatLeftProjection);
    }

    /**
     * Compatibility mode method that retrieves the current
     * compatibility mode projection transform for the right eye and
     * places it into the specified object.
     * @param projection the Transform3D object that will receive the
     * projection
     * @exception RestrictedAccessException if compatibility mode is disabled.
     */
    public void getRightProjection(Transform3D projection) {
	if (!compatibilityModeEnable) {
	    throw new RestrictedAccessException(J3dI18N.getString("View4"));
	}

	projection.set(compatRightProjection);
    }

    /**
     * Compatibility mode method that specifies the ViewPlatform
     * Coordinates (VPC) to Eye Coordinates (EC) transform.
     * If compatibility mode is disabled, then this transform
     * is derived from other values and is read-only.
     * @param vpcToEc the new VPC to EC transform
     * @exception RestrictedAccessException if compatibility mode is disabled.
     * @exception BadTransformException if the transform is not affine.
     */
    public void setVpcToEc(Transform3D vpcToEc) {
	if (!compatibilityModeEnable) {
	    throw new RestrictedAccessException(J3dI18N.getString("View6"));
	}

	if (!vpcToEc.isAffine()) {
	    throw new BadTransformException(J3dI18N.getString("View7"));
	}
	
	synchronized(this) {
	    compatVpcToEc.setWithLock(vpcToEc);
	    vDirtyMask |= View.COMPATIBILITY_MODE_DIRTY;
	}

	repaint();
    }

    /**
     * Compatibility mode method that retrieves the current
     * ViewPlatform Coordinates (VPC) system to
     * Eye Coordinates (EC) transform and copies it into the specified
     * object.
     * @param vpcToEc the object that will receive the vpcToEc transform.
     * @exception RestrictedAccessException if compatibility mode is disabled.
     */
    public void getVpcToEc(Transform3D vpcToEc) {
	if (!compatibilityModeEnable) {
	    throw new RestrictedAccessException(J3dI18N.getString("View8"));
	}

	vpcToEc.set(compatVpcToEc);
    }

    /**
     * Sets the view model's physical body to the PhysicalBody object provided.
     * Java 3D uses the parameters in the PhysicalBody to ensure accurate
     * image and sound generation when in head-tracked mode.
     * @param physicalBody the new PhysicalBody object
     */
    public void setPhysicalBody(PhysicalBody physicalBody) {
	// need to synchronize variable activateStatus
	synchronized (canvasList) {
	    if (activeStatus) {
		if (this.physicalBody != null) {
		    this.physicalBody.removeUser(this);
		}
		physicalBody.addUser(this);
	    }
	}
	this.physicalBody = physicalBody;
	repaint();
    }

    /**
     * Returns a reference to the view model's PhysicalBody object.
     * @return the view object's PhysicalBody object
     */
    public PhysicalBody getPhysicalBody() {
	return this.physicalBody;
    }

    /**
     * Sets the view model's physical environment to the PhysicalEnvironment
     * object provided.
     * @param physicalEnvironment the new PhysicalEnvironment object
     */
    public void setPhysicalEnvironment(PhysicalEnvironment physicalEnvironment) {
	synchronized (canvasList) {
	    if (activeStatus) {
		if (this.physicalEnvironment != null) {
		    this.physicalEnvironment.removeUser(this);
		}
		physicalEnvironment.addUser(this);
	    }
	}
	this.physicalEnvironment = physicalEnvironment;


	if ((viewPlatform != null) && viewPlatform.isLive()) {
	    VirtualUniverse.mc.postRequest(MasterControl.PHYSICAL_ENV_CHANGE, this);
	}
	repaint();
    }

    /**
     * Returns a reference to the view model's PhysicalEnvironment object.
     * @return the view object's PhysicalEnvironment object
     */
    public PhysicalEnvironment getPhysicalEnvironment() {
	return this.physicalEnvironment;
    }

    /**
     * Sets the screen scale value for this view.
     * This is used when the screen scale policy is SCALE_EXPLICIT.
     * The default value is 1.0 (i.e., unscaled).
     * @param scale the new screen scale
     */
    public void setScreenScale(double scale) {
	synchronized(this) {
	    this.screenScale = scale;
	    vDirtyMask |= View.SCREEN_SCALE_DIRTY;
	}
	repaint();
    }
    
    /**
     * Returns the current screen scale value
     * @return the current screen scale value
     */
    public double getScreenScale() {
	return this.screenScale;
    }

    /**
     * Sets the field of view used to compute the projection transform.
     * This is used when head tracking is disabled and when the Canvas3D's
     * windowEyepointPolicy is RELATIVE_TO_FIELD_OF_VIEW.
     * @param fieldOfView the new field of view in radians
     */
    public void setFieldOfView(double fieldOfView) {
	synchronized(this) {
	    this.fieldOfView = fieldOfView;
	    vDirtyMask |= View.FIELD_OF_VIEW_DIRTY;	    
	}
	repaint();

    }

    /**
     * Returns the current field of view.
     * @return the current field of view in radians
     */
    public double getFieldOfView() {
	return this.fieldOfView;
    }


    /**
     * Sets the position of the manual left eye in coexistence
     * coordinates.  This value determines eye placement when a head
     * tracker is not in use and the application is directly controlling
     * the eye position in coexistence coordinates.  This value is
     * ignored when in head-tracked mode or when the
     * windowEyePointPolicy is <i>not</i> RELATIVE_TO_COEXISTENCE.
     *
     * @param position the new manual left eye position
     *
     * @since Java 3D 1.2
     */
    public void setLeftManualEyeInCoexistence(Point3d position) {
	synchronized(this) {
	    leftManualEyeInCoexistence.set(position);
	    vDirtyMask |= View.LEFT_MANUAL_EYE_IN_COEXISTENCE_DIRTY;
	}
	repaint();
    }

    /**
     * Sets the position of the manual right eye in coexistence
     * coordinates.  This value determines eye placement when a head
     * tracker is not in use and the application is directly controlling
     * the eye position in coexistence coordinates.  This value is
     * ignored when in head-tracked mode or when the
     * windowEyePointPolicy is <i>not</i> RELATIVE_TO_COEXISTENCE.
     *
     * @param position the new manual right eye position
     *
     * @since Java 3D 1.2
     */
    public void setRightManualEyeInCoexistence(Point3d position) {
	synchronized(this) {
	    rightManualEyeInCoexistence.set(position);
	    vDirtyMask |= View.RIGHT_MANUAL_EYE_IN_COEXISTENCE_DIRTY;
	}
	repaint();
    }

    /**
     * Retrieves the position of the user-specified, manual left eye
     * in coexistence
     * coordinates and copies that value into the object provided.
     * @param position the object that will receive the position
     *
     * @since Java 3D 1.2
     */
    public void getLeftManualEyeInCoexistence(Point3d position) {
	position.set(leftManualEyeInCoexistence);
    }

    /**
     * Retrieves the position of the user-specified, manual right eye
     * in coexistence
     * coordinates and copies that value into the object provided.
     * @param position the object that will receive the position
     *
     * @since Java 3D 1.2
     */
    public void getRightManualEyeInCoexistence(Point3d position) {
	position.set(rightManualEyeInCoexistence);
    }


    /**
     * Sets the view model's front clip distance.
     * This value specifies the distance away from the eyepoint
     * in the direction of gaze where objects stop disappearing.
     * Objects closer to the eye than the front clip
     * distance are not drawn. The default value is 0.1 meters.
     * <p>
     * There are several considerations that need to be taken into
     * account when choosing values for the front and back clip
     * distances.
     * <ul>
     * <li>The front clip distance must be greater than
     * 0.0 in physical eye coordinates.</li>
     * <li>The front clipping plane must be in front of the
     * back clipping plane, that is, the front clip distance
     * must be less than the back clip distance in physical eye
     * coordinates.</li>
     * <li>The front and back clip distances, in physical
     * eye coordinates, must be less than the largest positive
     * single-precision floating point value, <code>Float.MAX_VALUE</code>.
     * In practice, since these physical eye coordinate distances are in
     * meters, the values should be <i>much</i> less than that.
     * <li>The ratio of the back distance divided by the front distance,
     * in physical eye coordinates, affects Z-buffer precision. This
     * ratio should be less than about 3000 in order to accommodate 16-bit
     * Z-buffers. Values of 100 to less than 1000 will produce better
     * results.</li>
     * </ul>
     * Violating any of the above rules will result in undefined
     * behavior. In many cases, no picture will be drawn.
     *
     * @param distance the new front clip distance
     * @see #setBackClipDistance
     */
    public void setFrontClipDistance(double distance) {
	synchronized(this) {
	    this.frontClipDistance = distance;
	    vDirtyMask |= View.CLIP_DIRTY;
	}
	repaint();
    }

    /**
     * Returns the view model's front clip distance.
     * @return the current front clip distance
     */
    public double getFrontClipDistance() {
	return this.frontClipDistance;
    }

    /**
     * Sets the view model's back clip distance.
     * The parameter specifies the distance from the eyepoint
     * in the direction of gaze to where objects begin disappearing.
     * Objects farther away from the eye than the
     * back clip distance are not drawn.
     * The default value is 10.0 meters.
     * <p>
     * There are several considerations that need to be taken into
     * account when choosing values for the front and back clip
     * distances. These are enumerated in the description of
     * <a href=#setFrontClipDistance(double)>setFrontClipDistance</a>.
     * <p>
     * Note that this attribute is only used if there is no Clip node
     * that is in scope of the view platform associated with this view.
     * @param distance the new back clip distance
     * @see #setFrontClipDistance
     * @see Clip#setBackDistance
     */
    public void setBackClipDistance(double distance) {
	synchronized(this) {
	    this.backClipDistance = distance;
	    vDirtyMask |= View.CLIP_DIRTY;
	}
	repaint();
    }

    /**
     * Returns the view model's back clip distance.
     * @return the current back clip distance
     */
    public double getBackClipDistance() {
	return this.backClipDistance;
    }

    /**
     * Retrieves the user-head to virtual-world transform
     * and copies that value into the transform provided.
     * @param t the Transform3D object that will receive the transform
     */
    public void getUserHeadToVworld(Transform3D t) {

        if( userHeadToVworldEnable ) {

           // get the calculated userHeadToVworld transform
           // from the view cache.
           // grab the first canvas -- not sure for multiple canvases
           Canvas3D canvas = (Canvas3D) this.canvases.firstElement();
           synchronized(canvas.canvasViewCache) {
              t.set(canvas.canvasViewCache.getHeadToVworld());
           }
        }else {
           throw new RestrictedAccessException(J3dI18N.getString("View9"));
        }
    }

    /**
     * Sets the view model's front clip policy, the policy Java 3D uses
     * in computing where to place the front clip plane. The variable 
     * can contain one of:
     * <UL>
     * <LI>VIRTUAL_EYE, to specify that the associated distance is
     * from the eye and in units of virtual distance
     * </LI>
     * <LI>PHYSICAL_EYE, to specify that the associated distance is
     * from the eye and in units of physical distance (meters)
     * </LI>
     * <LI>VIRTUAL_SCREEN, to specify that the associated distance is
     * from the screen and in units of virtual distance
     * </LI>
     * <LI>PHYSICAL_SCREEN, to specify that the associated distance is
     * from the screen and in units of physical distance (meters)
     * </LI>
     * </UL>
     * The default front clip policy is PHYSICAL_EYE.
     * @param policy the new policy, one of PHYSICAL_EYE, PHYSICAL_SCREEN,
     * VIRTUAL_EYE, or VIRTUAL_SCREEN
     */
    public void setFrontClipPolicy(int policy) {
	synchronized(this) {
	    this.frontClipPolicy = policy;
	    vDirtyMask |= View.CLIP_DIRTY;
	}
	repaint();
    }

    /**
     * Returns the view model's current front clip policy.
     * @return one of:
     * VIRTUAL_EYE, PHYSICAL_EYE, VIRTUAL_SCREEN, or PHYSICAL_SCREEN
     */
    public int getFrontClipPolicy() {
	return this.frontClipPolicy;
    }

    /**
     * Sets the view model's back clip policy, the policy Java 3D uses
     * in computing where to place the back clip plane. The variable 
     * can contain one of:
     * <UL>
     * <LI>VIRTUAL_EYE, to specify that the associated distance is
     * from the eye and in units of virtual distance
     * </LI>
     * <LI>PHYSICAL_EYE, to specify that the associated distance is
     * from the eye and in units of physical distance (meters)
     * </LI>
     * <LI>VIRTUAL_SCREEN, to specify that the associated distance is
     * from the screen and in units of virtual distance
     * </LI>
     * <LI>PHYSICAL_SCREEN, to specify that the associated distance is
     * from the screen and in units of physical distance (meters)
     * </LI>
     * </UL>
     * The default back clip policy is PHYSICAL_EYE.
     * @param policy the new policy, one of PHYSICAL_EYE, PHYSICAL_SCREEN,
     * VIRTUAL_EYE, or VIRTUAL_SCREEN
     */
    public void setBackClipPolicy(int policy) {
	synchronized(this) {
	    this.backClipPolicy = policy;
	    vDirtyMask |= View.CLIP_DIRTY;
	}
	repaint();
    }

    /**
     * Returns the view model's current back clip policy.
     * @return one of:
     * VIRTUAL_EYE, PHYSICAL_EYE, VIRTUAL_SCREEN, or PHYSICAL_SCREEN
     */
    public int getBackClipPolicy() {
	return this.backClipPolicy;
    }

    /**
     * Sets the visibility policy for this view.  This attribute 
     * is one of:
     * <UL>
     * <LI>VISIBILITY_DRAW_VISIBLE, to specify that only visible objects
     * are drawn.
     * </LI>
     * <LI>VISIBILITY_DRAW_INVISIBLE, to specify that only invisible objects
     * are drawn.
     * </LI>
     * <LI>VISIBILITY_DRAW_ALL, to specify that both visible and
     * invisible objects are drawn.
     * </LI>
     * </UL>
     * The default visibility policy is VISIBILITY_DRAW_VISIBLE.
     *
     * @param policy the new policy, one of VISIBILITY_DRAW_VISIBLE,
     * VISIBILITY_DRAW_INVISIBLE, or VISIBILITY_DRAW_ALL.
     *
     * @see RenderingAttributes#setVisible
     *
     * @since Java 3D 1.2
     */
    public void setVisibilityPolicy(int policy) {	

	synchronized(this) {
	    this.visibilityPolicy = policy;
	    vDirtyMask |= View.VISIBILITY_POLICY_DIRTY;
	}

	if (activeStatus && isRunning) {
	    
	    J3dMessage vpMessage = new J3dMessage();
	    vpMessage.universe = universe;
	    vpMessage.view = this;
	    vpMessage.type = J3dMessage.UPDATE_VIEW;
	    vpMessage.threads = J3dThread.UPDATE_RENDER;
	    vpMessage.args[0] = this;
	    synchronized(((ViewPlatformRetained)viewPlatform.retained).sphere) {
		vpMessage.args[1] = new Float(((ViewPlatformRetained)viewPlatform.
					       retained).sphere.radius);
	    }
	    vpMessage.args[2] = new Integer(OTHER_ATTRS_CHANGED);
	    vpMessage.args[3] = new Integer(transparencySortingPolicy);
	    VirtualUniverse.mc.processMessage(vpMessage);
	}
    }

    /**
     * Retrieves the current visibility policy.
     * @return one of:
     * VISIBILITY_DRAW_VISIBLE,
     * VISIBILITY_DRAW_INVISIBLE, or VISIBILITY_DRAW_ALL.
     *
     * @since Java 3D 1.2
     */
    public int getVisibilityPolicy() {
	return this.visibilityPolicy;
    }

    /**
     * Sets the transparency sorting policy for this view.  This attribute 
     * is one of:
     *
     * <UL>
     * <LI>TRANSPARENCY_SORT_NONE, to specify that no depth sorting of
     * transparent objects is performed.  Transparent objects are
     * drawn after opaque objects, but are not sorted from back to
     * front.</LI>
     *
     * <LI>TRANSPARENCY_SORT_GEOMETRY, to specify that transparent
     * objects are depth-sorted on a per-geometry basis.  Each
     * geometry object of each transparent Shape3D node is drawn from
     * back to front.  Note that this policy will not split geometry
     * into smaller pieces, so intersecting or intertwined objects may
     * not be sorted correctly.</LI>
     * </UL>
     *
     * The default policy is TRANSPARENCY_SORT_NONE.
     *
     * @param policy the new policy, one of TRANSPARENCY_SORT_NONE
     * or TRANSPARENCY_SORT_GEOMETRY.
     *
     * @since Java 3D 1.3
     */
    public void setTransparencySortingPolicy(int policy) {	
	if (policy == transparencySortingPolicy) {
	    return;
	}
	
	transparencySortingPolicy = policy;
	if (activeStatus && isRunning) {
	    
	    J3dMessage vpMessage = new J3dMessage();
	    vpMessage.universe = universe;
	    vpMessage.view = this;
	    vpMessage.type = J3dMessage.UPDATE_VIEW;
	    vpMessage.threads = J3dThread.UPDATE_RENDER;
	    vpMessage.args[0] = this;
	    vpMessage.args[1] = null;
	    vpMessage.args[2] = new Integer(TRANSP_SORT_POLICY_CHANGED);
	    vpMessage.args[3] = new Integer(policy);
	    VirtualUniverse.mc.processMessage(vpMessage);
	}
    }

    /**
     * Retrieves the current transparency sorting policy.
     * @return one of:
     * TRANSPARENCY_SORT_NONE or TRANSPARENCY_SORT_GEOMETRY.
     *
     * @since Java 3D 1.3
     */
    public int getTransparencySortingPolicy() {
	return this.transparencySortingPolicy;
    }

    /**
     * Turns head tracking on or off for this view.
     * @param flag specifies whether head tracking is enabled or
     * disabled for this view
     */
    public void setTrackingEnable(boolean flag) {

	synchronized(this) {
	    this.trackingEnable = flag;
	    vDirtyMask |= View.TRACKING_ENABLE_DIRTY;
	}
	
	repaint();
    }

    /**
     * Returns a status flag indicating whether or not head tracking
     * is enabled.
     * @return a flag telling whether head tracking is enabled
     */
    public boolean getTrackingEnable() {
	return this.trackingEnable;
    }

    /**
     * Turns on or off the continuous
     * updating of the userHeadToVworld transform.
     * @param flag enables or disables continuous updating
     */
    public void setUserHeadToVworldEnable(boolean flag) {

	synchronized(this) {
	    userHeadToVworldEnable = flag;
	    vDirtyMask |= View.USER_HEAD_TO_VWORLD_ENABLE_DIRTY;
	}
	repaint();
    }

    /**
     * Returns a status flag indicating whether or not
     * Java 3D is continuously updating the userHeadToVworldEnable transform.
     * @return a flag indicating if continuously updating userHeadToVworld
     */
    public boolean getUserHeadToVworldEnable() {
	return userHeadToVworldEnable;
    }

    /**
     * Computes the sensor to virtual-world transform
     * and copies that value into the transform provided.
     * The computed transforms takes points in the sensor's coordinate
     * system and produces the point's corresponding value in
     * virtual-world coordinates. 
     * @param sensor the sensor in question
     * @param t the object that will receive the transform
     */
    public void getSensorToVworld(Sensor sensor, Transform3D t) {
        // grab the first canvas -- not sure for multiple canvases
        Canvas3D canvas = (Canvas3D) this.canvases.firstElement(); 
        Transform3D localTrans = new Transform3D();
        synchronized(canvas.canvasViewCache) {
              t.set(canvas.canvasViewCache.getVworldToTrackerBase());
        }
        t.invert();
        sensor.getRead(localTrans);
        t.mul(localTrans);
    }

    /**
     * Retrieves the position of the specified Sensor's 
     * hotspot in virtual-world coordinates
     * and copies that value into the position provided.
     * This value is derived from other values and is read-only.
     * @param sensor the sensor in question
     * @param position the variable that will receive the position
     */
    public void getSensorHotspotInVworld(Sensor sensor,
					 Point3f position) {

        Canvas3D canvas = (Canvas3D) this.canvases.firstElement(); 
        Transform3D sensorToVworld = new Transform3D();
        Point3d hotspot3d = new Point3d();

	getSensorToVworld(sensor, sensorToVworld);
        sensor.getHotspot(hotspot3d);
        position.set(hotspot3d);
        sensorToVworld.transform(position);
    }

    /**
     * Retrieves the position of the specified Sensor's 
     * hotspot in virtual-world coordinates
     * and copies that value into the position provided.
     * This value is derived from other values and is read-only.
     * @param sensor the sensor in question
     * @param position the variable that will receive the position
     */
    public void getSensorHotspotInVworld(Sensor sensor,
					 Point3d position) {

        Canvas3D canvas = (Canvas3D) this.canvases.firstElement(); 
        Transform3D sensorToVworld = new Transform3D();

	getSensorToVworld(sensor, sensorToVworld);
        sensor.getHotspot(position);
        sensorToVworld.transform(position);
    }

    /**
     * Sets given Canvas3D at the given index position.
     * @param canvas3D the given Canvas3D to be set
     * @param index the position to be set
     * @exception IllegalStateException if the specified canvas is
     * a stereo canvas with a monoscopicEyePolicy of CYCLOPEAN_EYE_VIEW,
     * and the viewPolicy for this view is HMD_VIEW
     * @exception IllegalSharingException if the specified canvas is
     * associated with another view
     */
    public void setCanvas3D(Canvas3D canvas3D, int index) {
	
	if((viewPolicy == HMD_VIEW) &&
	   (canvas3D.monoscopicViewPolicy == View.CYCLOPEAN_EYE_VIEW) &&
	   (!canvas3D.useStereo)){
	    
	    throw new
		IllegalStateException(J3dI18N.getString("View31"));
	}
	
	Canvas3D cv;
	
	synchronized(canvasList) {
            if (canvas3D.getView() != null)
		throw new IllegalSharingException(J3dI18N.getString("View10"));
	    cv = (Canvas3D) canvases.elementAt(index);
	    canvases.setElementAt(canvas3D, index);
	    removeFromCanvasList(cv);
	    addToCanvasList(canvas3D);
	    canvasesDirty = true;
	}

	canvas3D.setView(this);
	cv.setView(null);

	if (canvas3D.added) {
	    evaluateActive();
	} 
	if (cv.added) {
	    evaluateActive();
	}

    }

    /**
     * Gets the Canvas3D at the specified index position.
     * @param index the position from which to get Canvas3D object
     * @return the Canvas3D at the sprcified index position
     */
    public Canvas3D getCanvas3D(int index){
	return (Canvas3D) this.canvases.elementAt(index);
    }

    /**
     * Gets the enumeration object of all the Canvas3Ds.
     * @return the enumeration object of all the Canvas3Ds.
     */
    public Enumeration getAllCanvas3Ds(){
        return canvases.elements();
    }

    /**
     * Returns the number of Canvas3Ds in this View.
     * @return the number of Canvas3Ds in this View
     *
     * @since Java 3D 1.2
     */
    public int numCanvas3Ds() {
        return canvases.size();
    }

    /**
     * Adds the given Canvas3D at the end of the list.
     * @param canvas3D the Canvas3D to be added
     * @exception IllegalStateException if the specified canvas is
     * a stereo canvas with a monoscopicEyePolicy of CYCLOPEAN_EYE_VIEW,
     * and the viewPolicy for this view is HMD_VIEW
     * @exception IllegalSharingException if the specified canvas is
     * associated with another view
     */
    public void addCanvas3D(Canvas3D canvas3D){

	if((viewPolicy == HMD_VIEW) &&
	   (canvas3D.monoscopicViewPolicy == View.CYCLOPEAN_EYE_VIEW) &&
	   (!canvas3D.useStereo)) {
	    throw new
		IllegalStateException(J3dI18N.getString("View31"));
	}

	synchronized(canvasList) {
            if (canvas3D.getView() != null)
		throw new IllegalSharingException(J3dI18N.getString("View10"));
	    canvases.addElement(canvas3D);
	    addToCanvasList(canvas3D);
	    canvasesDirty = true;
	}

	canvas3D.setView(this);

	if (canvas3D.added) {
	    if ((canvas3D.visible || canvas3D.offScreen) &&
		canvas3D.firstPaintCalled) {
		canvas3D.active = true;
	    }
	    evaluateActive();
	}
    }

    /**
     * Inserts the Canvas3D at the given index position.
     * @param canvas3D the Canvas3D to be inserted
     * @param index the position to be inserted at
     * @exception IllegalStateException if the specified canvas is
     * a stereo canvas with a monoscopicEyePolicy of CYCLOPEAN_EYE_VIEW,
     * and the viewPolicy for this view is HMD_VIEW
     * @exception IllegalSharingException if the specified canvas is
     * associated with another view
     */
    public void insertCanvas3D(Canvas3D canvas3D, int index){

	if((viewPolicy == HMD_VIEW) &&
	   (canvas3D.monoscopicViewPolicy == View.CYCLOPEAN_EYE_VIEW) &&
	   (!canvas3D.useStereo)) {
	    throw new
		IllegalStateException(J3dI18N.getString("View31"));
	}
	
	synchronized(canvasList) {
            if (canvas3D.getView() != null)
	     throw new IllegalSharingException(J3dI18N.getString("View10"));
	    this.canvases.insertElementAt(canvas3D, index);
	    addToCanvasList(canvas3D);
	    canvasesDirty = true;
	}

	canvas3D.setView(this);

	if (canvas3D.added) {
	    if ((canvas3D.visible || canvas3D.offScreen) &&
		canvas3D.firstPaintCalled) {
		canvas3D.active = true;
	    }
	    evaluateActive();
	}
    }

    /**
     * Removes the Canvas3D from the given index position.
     * @param index the position of Canvas3D object to be removed
     */
    public void removeCanvas3D(int index) {
	// index -1 is possible if the view is unregistered first 
	// because viewPlatform is clearLived,
	// and then removeCanvas from the view
	if (index == -1)
	    return;

	Canvas3D cv;

	synchronized(canvasList) {
	    cv = (Canvas3D) canvases.elementAt(index);

	    canvases.removeElementAt(index);
	    removeFromCanvasList(cv);
	    canvasesDirty = true;
	}

	// reset canvas will set view to null also
	VirtualUniverse.mc.postRequest(MasterControl.RESET_CANVAS, 
                                      cv);
	cv.pendingView = null;

	computeCanvasesCached();

	if (cv.added) {
	    cv.active = false;
	    evaluateActive();
	}
	if (universe != null) {
	    universe.waitForMC();
	}
    }


    /**
     * Retrieves the index of the specified Canvas3D in
     * this View's list of Canvas3Ds
     *
     * @param canvas3D the Canvas3D to be looked up.
     * @return the index of the specified Canvas3D;
     * returns -1 if the object is not in the list.
     *
     * @since Java 3D 1.3
     */
    public int indexOfCanvas3D(Canvas3D canvas3D) {
	return canvases.indexOf(canvas3D);
    }


    /**
     * Removes the specified Canvas3D from this View's
     * list of Canvas3Ds.
     * If the specified object is not in the list, the list is not modified.
     *
     * @param canvas3D the Canvas3D to be removed.
     */
    public void removeCanvas3D(Canvas3D canvas3D) {
	removeCanvas3D(canvases.indexOf(canvas3D));
    }


    /**
     * Removes all Canvas3Ds from this View.
     *
     * @since Java 3D 1.3
     */
    public void removeAllCanvas3Ds() {
	LinkedList tmpCanvases = new LinkedList();

	synchronized(canvasList) {
	    int numCanvases = canvases.size();

	    // Remove in reverse order to ensure valid indices
	    for (int index = numCanvases - 1; index >= 0; index--) {
		Canvas3D cv;

		cv = (Canvas3D) canvases.elementAt(index);

		// Record list of canvases to be deleted;
		tmpCanvases.add(cv);

		canvases.removeElementAt(index);
		removeFromCanvasList(cv);
		canvasesDirty = true;
	    }
	}

	// ISSUE 83: postRequest must *not* be called while holding
	// canvasList lock. Holding the lock can cause a deadlock.

	Iterator iterator = tmpCanvases.iterator();
	while (iterator.hasNext()) {
	    Canvas3D cv = (Canvas3D)iterator.next();

	    // reset canvas will set view to null also
	    VirtualUniverse.mc.postRequest(MasterControl.RESET_CANVAS, 
					   cv);
	    cv.pendingView = null;

	    if (cv.added) {
		cv.active = false;
	    }
	}

	computeCanvasesCached();

	evaluateActive();

	if (universe != null) {
	    universe.waitForMC();
	}
    }


    // This adds this canvas and its screen to the screen list. 
    // Locks are already acquired before this is called.
    private void addToCanvasList(Canvas3D c) {

	for (int i=screenList.size()-1; i>=0; i--) {
	    if ((Screen3D)screenList.get(i) == c.screen) {
		// This is the right screen slot
		((ArrayList)canvasList.get(i)).add(c);
		canvasesDirty = true;
		return;
	    }
	}
	
	// Add a screen slot
	screenList.add(c.screen);
	ArrayList clist = new ArrayList();
	canvasList.add(clist);
	clist.add(c);
	canvasesDirty = true;
    }

    // This removes this canvas and its screen from the screen list
    // Locks are already acquired before this is called.
    private void removeFromCanvasList(Canvas3D c) {

	for (int i=screenList.size()-1; i>=0; i--) {
	    if ((Screen3D) screenList.get(i) == c.screen) {
		// This is the right screen slot
		ArrayList clist = (ArrayList)canvasList.get(i);
		clist.remove(clist.indexOf(c));
	
		if (clist.size() == 0) {
		    canvasList.remove(i);
		    screenList.remove(i);
		    canvasesDirty = true;
		}
		return;
	    }
	}
    }

    // Locks are not acquired before this is called.
    void computeCanvasesCached() {

        synchronized (canvasList) {
	    ArrayList cv;
	    int len = canvases.size();

	    Canvas3D newCachedCanvases[] = new Canvas3D[len];
	    for (int i=0; i < len; i++) {
		newCachedCanvases[i] = (Canvas3D) canvases.get(i);
	    }
	    // Do this in one instruction so there is no need to
	    // synchronized getCanvases()

	    cachedCanvases = newCachedCanvases;
	    len = 0;
	    longestScreenList = 0;
	    cachedCanvasList = new Canvas3D[canvasList.size()][0];
	    for (int i=0; i < cachedCanvasList.length; i++) {
		cv = (ArrayList) canvasList.get(i);
		len = cv.size();
		cachedCanvasList[i] = new Canvas3D[len];
		for (int j=0; j < len; j++) {
		    cachedCanvasList[i][j] = (Canvas3D) cv.get(j);
		}

		if (len > longestScreenList) {
		    longestScreenList = len;
		}
	    }
	    len = screenList.size();
	    Screen3D newCachedScreens[] = new Screen3D[len];
	
	    for (int i=0; i < len; i++) {
		newCachedScreens[i] = (Screen3D) screenList.get(i);
	    }
	    // Do this in one instruction so there is no need to
	    // synchronized getScreens()
	    cachedScreens = newCachedScreens;
	    canvasesDirty = false;
	}
    }
    
    // This creates a 2 dimentional list of canvases
    // ONLY MC can call this procedure with canCompute=true, 
    // since MC want the result return by
    // evaluateCanvases and updateWorkThreads agree to each other,
    // so only evaluateCanvases can compute a new list.
    // Other threads should use getCanvasList(false).
    Canvas3D[][] getCanvasList(boolean canCompute) {
	if (canvasesDirty && canCompute) {
	    computeCanvasesCached();
	}
	return cachedCanvasList;
    }

    // assume getCanvasList is called before
    int getLongestScreenList() {
	return longestScreenList;
    }

    // assume getCanvasList is called before
    Canvas3D[] getCanvases() {
	return cachedCanvases;
    }

    // assume getCanvasList is called before
    Screen3D[] getScreens() {
	return cachedScreens;
    }

    Canvas3D getFirstCanvas() {
	synchronized (canvasList) {
	    if (canvases.size() > 0) {
		return (Canvas3D) canvases.elementAt(0);
	    }
	    return null;
	}
    }

    /**
     * This method returns the time at which the most recent rendering
     * frame started.  It is defined as the number of milliseconds
     * since January 1, 1970 00:00:00 GMT.
     * Since multiple canvases might be attached to this View,
     * the start of a frame is defined as the point in time just prior
     * to clearing any canvas attached to this view.
     * @return the time at which the most recent rendering frame started
     */
    public long getCurrentFrameStartTime() {
	synchronized (frameStartTimes) {
	    return currentFrameStartTime;
	}
    }

    /**
     * This method returns the duration, in milliseconds, of the most
     * recently completed rendering frame.  The time taken to render
     * all canvases attached to this view is measured.  This duration
     * is computed as the difference between the start of the most recently
     * completed frame and the end of that frame.
     * Since multiple canvases might be attached to this View,
     * the start of a frame is defined as the point in time just prior
     * to clearing any canvas attached to this view--before preRender
     * is called for any canvas.  Similarly, the end of a frame is
     * defined as the point in time just after swapping the buffer for
     * all canvases--after postSwap is called for all canvases.
     * Note that since the frame duration is measured from start to stop
     * for this view only, the value returned is not the same as
     * frame rate; it measures only the rendering time for this view.
     *
     * @return the duration, in milliseconds, of the most recently
     * completed rendering frame
     */
    public long getLastFrameDuration() {
	synchronized (frameStartTimes) {
	    return currentFrameDuration;
	}
    }

    /**
     * This method returns the frame number for this view.  The frame
     * number starts at 0 and is incremented at the start of each
     * frame--prior to clearing all the canvases attached to this
     * view.
     *
     * @return the current frame number for this view
     */
    public long getFrameNumber() {
	synchronized (frameStartTimes) {
	    return currentFrameNumber;
	}
    }

    /**
     * Retrieves the implementation-dependent maximum number of
     * frames whose start times will be recorded by the system.  This
     * value is guaranteed to be at least 10 for all implementations
     * of the Java 3D API.
     * @return the maximum number of frame start times recorded
     */
    public static int getMaxFrameStartTimes() {
	return (NUMBER_FRAME_START_TIMES);
    }

    /**
     * Copies the last <i>k</i> frame start time values into
     * the user-specified array.  The most recent frame start time is
     * copied to location 0 of the array, the next most recent frame
     * start time is copied into location 1 of the array, and so forth.
     * If times.length is smaller than
     * maxFrameStartTimes, then only the last times.length values are
     * copied.  If times.length is greater than maxFrameStartTimes,
     * then all array elements after index maxFrameStartTimes-1 are
     * set to 0.
     *
     * @return the frame number of the most recent frame in the array
     *
     * @see #setMinimumFrameCycleTime
     */
    public long getFrameStartTimes(long[] times) {
	int index, i, loopCount;
	long lastFrameNumber;

	synchronized (frameStartTimes) {
	    index = currentFrameIndex - 1;
	    if (index < 0) {
	        index = NUMBER_FRAME_START_TIMES - 1;
	    }
	    lastFrameNumber = frameNumbers[index];

	    if (times.length <= NUMBER_FRAME_START_TIMES) {
	        loopCount = times.length;
	    } else {
	        loopCount = NUMBER_FRAME_START_TIMES;
	    }

            for (i=0; i<loopCount; i++) {
                times[i] = frameStartTimes[index];
                index--;
                if (index < 0) {
                    index = NUMBER_FRAME_START_TIMES - 1;
                }
            }
    
	    if (times.length > NUMBER_FRAME_START_TIMES) {
	        for (; i<times.length; i++) {
		    times[i] = 0;
	        }
	    }
	}

	return (lastFrameNumber);
    }

    /**
     * Sets the minimum frame cycle time, in milliseconds, for this
     * view.  The Java 3D renderer will ensure that the time between
     * the start of each successive frame is at least the specified
     * number of milliseconds.  The default value is 0.
     *
     * @param minimumTime the minimum number of milliseconds between
     * successive frames
     *
     * @exception IllegalArgumentException if <code>minimumTime < 0</code>
     *
     * @see #getFrameStartTimes
     *
     * @since Java 3D 1.2
     */
    public void setMinimumFrameCycleTime(long minimumTime) {
	if (minimumTime < 0L)
	    throw new IllegalArgumentException(J3dI18N.getString("View27"));

	minFrameCycleTime = minimumTime;
	VirtualUniverse.mc.setWork();
    }

    /**
     * Retrieves the minimum frame cycle time, in milliseconds, for this view.
     * @return the minimum frame cycle time for this view.
     *
     * @see #getFrameStartTimes
     *
     * @since Java 3D 1.2
     */
    public long getMinimumFrameCycleTime() {
	return minFrameCycleTime;
    }


    /**
     * This adds a frame time to the this of frame times
     */
    void setFrameTimingValues() {
	
	synchronized (frameStartTimes) {
	    if (currentFrameIndex == NUMBER_FRAME_START_TIMES) {
		currentFrameIndex = 0;
	    }

	    frameNumbers[currentFrameIndex] = frameNumber;

	    frameStartTimes[currentFrameIndex++] = startTime;
	    currentFrameStartTime = startTime;
	    currentFrameDuration = stopTime - startTime;
	    currentFrameNumber = frameNumber;
	}
    }

    /**
     * Return true if maximum fps impose by user reach
     */
    void computeCycleTime() {
	if (minFrameCycleTime == 0) {
	    isMinCycleTimeAchieve = true;
	    sleepTime = 0;
	} else {
	    sleepTime = minFrameCycleTime - 
		(J3dClock.currentTimeMillis() - startTime);
	    isMinCycleTimeAchieve = (sleepTime <= 0);
	}
    }


    /**
     * Enables or disables automatic freezing of the depth buffer for
     * objects rendered
     * during the transparent rendering pass (i.e., objects rendered
     * using alpha blending) for this view.
     * If enabled, depth buffer writes will be disabled during the
     * transparent rendering pass regardless of the value of
     * the depth buffer write enable flag in the RenderingAttributes
     * object for a particular node.
     * This flag is enabled by default.
     * @param flag indicates whether automatic freezing of the depth buffer
     * for transparent/antialiased objects is enabled.
     * @see RenderingAttributes#setDepthBufferWriteEnable
     */
    public void setDepthBufferFreezeTransparent(boolean flag) {
	depthBufferFreezeTransparent = flag;
	repaint();
    }

    /**
     * Retrieves the current value of the depth buffer freeze transparent
     * flag for this view.
     * @return a flag that indicates whether or not the depth
     * buffer is automatically frozen during the transparent rendering pass.
     */
    public boolean getDepthBufferFreezeTransparent() {
	return depthBufferFreezeTransparent;
    }

    /**
     * Enables or disables scene antialiasing for this view.
     * If enabled, the entire scene will be antialiased on
     * each canvas in which scene antialiasing is available.
     * Scene antialiasing is disabled by default.
     * <p>
     * NOTE: Scene antialiasing is ignored in pure immediate mode,
     * but is supported in mixed-immediate mode.
     * @param flag indicates whether scene antialiasing is enabled
     *
     * @see Canvas3D#queryProperties
     */
    public void setSceneAntialiasingEnable(boolean flag) {
	 sceneAntialiasingEnable = flag;
	 repaint();
    }

    /**
     * Returns a flag that indicates whether or not scene antialiasing
     * is enabled for this view.
     * @return a flag that indicates whether scene antialiasing is enabled
     */
    public boolean getSceneAntialiasingEnable() {
	return sceneAntialiasingEnable;
    }

    /**
     * Sets a flag that indicates whether the local eyepoint is used in
     * lighting calculations for perspective projections.
     * If this flag is set to true, the view vector is calculated per-vertex
     * based on the direction from the actual eyepoint to the vertex.
     * If this flag is set to false, a single view vector is computed from
     * the eyepoint to the center of the view frustum.  This is
     * called infinite eye lighting.
     * Local eye lighting is disabled by default, and is ignored for
     * parallel projections.
     * @param flag indicates whether local eye lighting is enabled
     */
    public void setLocalEyeLightingEnable(boolean flag) {
	localEyeLightingEnable = flag;
	repaint();
    }

    /**
     * Retrieves a flag that indicates whether or not local eye lighting
     * is enabled for this view.
     * @return a flag that indicates whether local eye lighting is enabled
     */
    public boolean getLocalEyeLightingEnable() {
	return localEyeLightingEnable;
    }

    /**
     * Attach viewPlatform structure to this view.
     * @param vp the viewPlatform to be attached
     */
    public void attachViewPlatform(ViewPlatform vp) {

	if ((vp != null) && (vp == viewPlatform)) {
	    return;
	}

	if (viewPlatform != null) {
	    ((ViewPlatformRetained)viewPlatform.retained).removeView(this);
	    if (viewPlatform.isLive()) {
		synchronized (evaluateLock) {
		    viewPlatform = null;
		    // cleanup View stuff for the old platform
		    evaluateActive();
		    viewPlatform = vp;
		}
		if (universe != null) {
		    universe.waitForMC();
		}
	    } else {
		viewPlatform = vp;
	    }
	} else {
	    viewPlatform = vp;
	}
	if (vp != null) {
	    if (vp.isLive()) {
		checkView();
	        setUniverse(((ViewPlatformRetained)vp.retained).universe);
	    }
	    ((ViewPlatformRetained)vp.retained).setView(this);
	} 

	evaluateActive();
	if ((vp == null) && (universe != null)) {
	    universe.waitForMC();
	}
    }
    
    /**
     * Retrieves the currently attached ViewPlatform object
     * @return the currently attached ViewPlatform
     */
    public ViewPlatform getViewPlatform() {
	return viewPlatform;
    }

    /**
     * Checks view parameters for consistency
     */
    void checkView() {
	if (physicalBody == null)
	    throw new IllegalStateException(J3dI18N.getString("View13"));

	if (physicalEnvironment == null)
	    throw new IllegalStateException(J3dI18N.getString("View14"));
    }


    /**
     * Stops the behavior scheduler after all
     * currently scheduled behaviors are executed.  Any frame-based
     * behaviors scheduled to wake up on the next frame will be
     * executed at least once before the behavior scheduler is
     * stopped.
     * <p>
     * NOTE: This is a heavy-weight method
     * intended for verification and image capture (recording); it
     * is <i>not</i> intended to be used for flow control.
     * @return a pair of integers that specify the beginning and ending
     * time (in milliseconds since January 1, 1970 00:00:00 GMT)
     * of the behavior scheduler's last pass
     * @exception IllegalStateException if this method is called
     * from a Behavior method or from any Canvas3D render callback
     * method
     */
    public final long[] stopBehaviorScheduler() {
	long[] intervalTime = new long[2];

	if (checkBehaviorSchedulerState("View15", "View16")) {
	    if (activeStatus && isRunning &&
		(universe.behaviorScheduler != null)) {
		// view is active
		universe.behaviorScheduler.stopBehaviorScheduler(intervalTime);
	    } else {
		if ((universe != null) &&
		    (universe.behaviorScheduler != null)) {
		    universe.behaviorScheduler.userStop = true;
		}
	    }
	} 
	stopBehavior = true;
	return intervalTime;
    }

    /**
     * Starts the behavior scheduler running after it has been stopped.
     * @exception IllegalStateException if this method is called
     * from a Behavior method or from any Canvas3D render callback
     * method
     */
    public final void startBehaviorScheduler() {
	if (checkBehaviorSchedulerState("View17", "View18")) {
	    if (activeStatus && isRunning &&
		(universe.behaviorScheduler != null)) {
		universe.behaviorScheduler.startBehaviorScheduler();
		
	    } else {
		if ((universe != null) && 
		    (universe.behaviorScheduler != null)) {
		    universe.behaviorScheduler.userStop = false;
		}
	    }
	} 

	stopBehavior = false;
    }

    /**
     * Check if BehaviorScheduler is in valid state to start/stop
     * itself. 
     * @param s1 Exception String if method is called from a Canvas3D
     * @param s2 Exception String if method is called from a Behavior method
     * @return true if viewPlatform is live
     * @exception IllegalStateException if this method is called
     * from a Behavior method or from any Canvas3D render callback
     * method
     *
     */
    boolean checkBehaviorSchedulerState(String s1, String s2) {
	Thread me = Thread.currentThread();

	if (inCanvasCallback) {
	    synchronized (canvasList) {
		for (int i=canvases.size()-1; i>=0; i--) {
		    if (((Canvas3D)canvases.elementAt(i)).screen.renderer == me) {
			throw new IllegalStateException(J3dI18N.getString(s1));
		    }
		}
	    }
	}

	if ((viewPlatform != null) && viewPlatform.isLive()) {
	    if (universe.inBehavior && (universe.behaviorScheduler == me)) {
		throw new IllegalStateException(J3dI18N.getString(s2));
	    }
	    return true;
	}
	return false;
    }

    /**
     * Retrieves a flag that indicates whether the behavior scheduler is
     * currently running.
     * @return true if the behavior scheduler is running, false otherwise
     * @exception IllegalStateException if this method is called
     * from a Behavior method or from any Canvas3D render callback
     * method
     */
    public final boolean isBehaviorSchedulerRunning() {
	return  (((universe != null) && !stopBehavior &&
		  (universe.behaviorScheduler != null)) ?
		 !universe.behaviorScheduler.userStop : false);
    }

    /**
     * Stops traversing the scene graph for this
     * view after the current state of the scene graph is reflected on
     * all canvases attached to this view.  The renderers associated
     * with these canvases are also stopped.
     * <p>
     * NOTE: This is a heavy-weight method
     * intended for verification and image capture (recording); it
     * is <i>not</i> intended to be used for flow control.
     * @exception IllegalStateException if this method is called
     * from a Behavior method or from any Canvas3D render callback
     * method
     */
    public final void stopView() {
	checkViewState("View19", "View20");
	synchronized (startStopViewLock) {
	    if (activeStatus && isRunning) {
		VirtualUniverse.mc.postRequest(MasterControl.STOP_VIEW, this);
		while (isRunning) {
		    MasterControl.threadYield();
		}
	    } else {
		isRunning = false;
	    }
	}
    }

    /**
     * Starts
     * traversing this view, and starts the renderers associated
     * with all canvases attached to this view.
     * @exception IllegalStateException if this method is called
     * from a Behavior method or from any Canvas3D render callback
     * method
     */
    public final void startView() {

	checkViewState("View21", "View22");
	synchronized (startStopViewLock) {
	    if (activeStatus && !isRunning) {
		VirtualUniverse.mc.postRequest(MasterControl.START_VIEW, this);
		while (!isRunning) {
		    MasterControl.threadYield();
		}
		VirtualUniverse.mc.sendRunMessage(this,
						  J3dThread.RENDER_THREAD);
	    } else {
		isRunning = true;
	    }
	}

    }

    /**
     *  This will throw IllegalStateException if not in valid state
     *  for start/stop request.
     */
    void checkViewState(String s1, String s2)  throws IllegalStateException {
	if (inCanvasCallback) {
	    Thread me = Thread.currentThread();
	    synchronized (canvasList) {
		for (int i= canvases.size()-1; i>=0; i--) {
		    Canvas3D cv = (Canvas3D)canvases.elementAt(i);
		    if (cv.screen.renderer == me) {
			throw new
			    IllegalStateException(J3dI18N.getString(s1));
		    }
		}
	    }
	}

	if ((viewPlatform != null) &&  viewPlatform.isLive()) {
	    if (universe.inBehavior &&
		(Thread.currentThread() ==  universe.behaviorScheduler)) {
	            throw new IllegalStateException(J3dI18N.getString(s2));
	    }
	}
    }

    /**
     * Retrieves a flag that indicates whether the traverser is
     * currently running on this view.
     * @return true if the traverser is running, false otherwise
     * @exception IllegalStateException if this method is called
     * from a Behavior method or from any Canvas3D render callback
     * method
     */
    public final boolean isViewRunning() {
	return isRunning;
    }

    /**
     * Renders one frame for a stopped View.  Functionally, this
     * method is equivalent to <code>startView()</code> followed by
     * <code>stopview()</code>, except that it is atomic, which
     * guarantees that only one frame is rendered.
     *
     * @exception IllegalStateException if this method is called from
     * a Behavior method or from any Canvas3D render callback, or if
     * the view is currently running.
     *
     * @since Java 3D 1.2
     */
    public void renderOnce() {
	checkViewState("View28", "View29");
	synchronized (startStopViewLock) {
	    if (isRunning) {
		throw new IllegalStateException(J3dI18N.getString("View30"));
	    }
	    renderOnceFinish = false;
	    VirtualUniverse.mc.postRequest(MasterControl.RENDER_ONCE, this);
	    while (!renderOnceFinish) {
		MasterControl.threadYield();
	    }
	    renderOnceFinish = true;
	}
    }

    /**
     * Requests that this View be scheduled for rendering as soon as
     * possible.  The repaint method may return before the frame has
     * been rendered.  If the view is stopped, or if the view is
     * continuously running (for example, due to a free-running
     * interpolator), this method will have no effect.  Most
     * applications will not need to call this method, since any
     * update to the scene graph or to viewing parameters will
     * automatically cause all affected views to be rendered.
     *
     * @since Java 3D 1.2
     */
    public void repaint() {
	if (activeStatus && isRunning) {
	    VirtualUniverse.mc.sendRunMessage(this,
					      J3dThread.RENDER_THREAD);
	}
    }


    /**
     * Update the view cache associated with this view.  Also, shapshot
     * the per-screen parameters associated with all screens attached
     * to this view.
     */
    final void updateViewCache() {


        // TODO KCR : remove obsolete DVR code (but make sure we don't end
        // up with a leak in the Viewer Map object).

        // DVR support
	// This is a back door in j3d to provide DVR support.
	// A better place to put this code segment is in
	// ViewCache.snapshot(). Since it consists of some 
	// back door code, I've decided to put it here to isolate
	// it from the rest of view snapshot code.
	if(firstTime) {
	    // System.err.println("View : First Time is " + firstTime);
	    // viewer = Viewer.getViewer(this);
	    // Since we've the handler to the viewer, we can remove the entry 
	    // now to avoid confusion and prevent memory leak problem.
	    viewer = Viewer.removeViewerMapEntry(this);	    
	    firstTime = false;
	}
	
//	if(viewer != null)  {
//	    if(viewer.isDvrEnabled()) {
//		dvrFactor = viewer.getDvrFactor();
//		dvrResizeCompensation = 
//		    viewer.getDvrResizeCompensationEnable();
//		/*
//		System.err.println("View : dvrFactor is " + dvrFactor);
//		System.err.println("View : dvrResizeCompensation is " + 
//				   dvrResizeCompensation);		 
//		*/
//	    }
//	    else {
//		// Reset back to default.
//		dvrFactor = 1.0f;
//		dvrResizeCompensation = true;
//
//	    }
//	}
	// End of back door -- DVR.
	    
	synchronized(this) {
	    viewCache.snapshot();
	    viewCache.computeDerivedData();
	}

	// Just take the brute force approach and snapshot the
	// parameters for each screen attached to each canvas.  We won't
	// worry about whether a screen is cached more than once.
	// Eventually, dirty bits will take care of this.

	synchronized (canvasList) {
	    int i = canvases.size()-1;
	    while (i>=0) {
		Screen3D scr =
		    ((Canvas3D)canvases.elementAt(i--)).getScreen3D();
		if (scr != null)
		    scr.updateViewCache();
	    }
	}
    }


    /** 
     * This routine activates or deactivates a view based on various information
     */
    void evaluateActive() {

	synchronized (evaluateLock) {
	    if (universe == null) {
		return;
	    }

	    if ((viewPlatform == null) || 
		!viewPlatform.isLive() ||
		!((ViewPlatformRetained)viewPlatform.retained).switchState.currentSwitchOn) {
		if (activeStatus) {
		    deactivate();
		    activeStatus = false;
		}
		// Destroy threads from MC
		if (VirtualUniverse.mc.isRegistered(this) &&
		    (universe.isEmpty() || 
		     (canvases.isEmpty() && 
		      ((viewPlatform == null) ||
		       !viewPlatform.isLive())))) {
		    // We can't wait until MC finish unregister view
		    // here because user thread may
		    // holds the universe.sceneGraphLock if branch
		    // or locale remove in clearLive(). In this way
		    // There is deadlock since MC also need need
		    // sceneGraphLock in some threads 
		    // (e.g. TransformStructure update thread)
		    universe.unRegViewWaiting = this;
		    resetUnivCount = universeCount;
		    VirtualUniverse.mc.postRequest(
					   MasterControl.UNREGISTER_VIEW, this);
		}
	    } else {

		// We're on a live view platform.  See what the canvases say
		// If view not register, MC will register it automatically
	    
		int i;
		VirtualUniverse u = null;
		synchronized (canvasList) {

		    for (i=canvases.size()-1; i>=0; i--) {
			Canvas3D cv = (Canvas3D)canvases.elementAt(i);
			if (cv.active) {

			    if (!activeStatus && (universeCount > resetUnivCount)) {
				u = universe;
			    }
			    break;
			}
		    }
		}

		// We should do this outside canvasList lock,
		// otherwise it may cause deadlock with MC
		if (u != null) {
		    activate(u);
		    activeStatus = true;
		    return;
		}

		
		if ((i < 0) && activeStatus) {
		    deactivate();
		    activeStatus = false;
		    return;
		}
	    
		if (VirtualUniverse.mc.isRegistered(this)) {
		    // notify MC that canvases state for this view changed
		    VirtualUniverse.mc.postRequest(
				   MasterControl.REEVALUATE_CANVAS, this);
		}
	    }
	}
    }

    void setUniverse(VirtualUniverse universe) {
	
	synchronized (VirtualUniverse.mc.requestObjList) {	
	    if ((renderBin == null) || 
		(renderBin.universe != universe)) {
		if (renderBin != null) {
		    renderBin.cleanup();
		}
		renderBin = new RenderBin(universe, this);
		renderBin.universe = universe;	
	    }
	
	
	    if ((soundScheduler == null) || 
		(soundScheduler.universe !=  universe)) {
		// create a sound scheduler for this view, with this universe
		if (soundScheduler != null) {
		    soundScheduler.cleanup();
		}
		soundScheduler = new SoundScheduler(universe, this);
	    }
	    
	    
	    // This has to be the last call before 
	    // RenderBin and SoundScheduler construct. Since it is
	    // possible that canvas receive paint call and invoked
	    // evaluateActive in another thread - which check for
	    // universe == null and may let it pass before soundScheduler
	    // and renderBin initialize.
	    universeCount++;
	    this.universe = universe;
	}
	evaluateActive();
    }

    /**
     * This activates all traversers and renderers associated with this view.
     */
    void activate(VirtualUniverse universe) {

	universe.checkForEnableEvents();

	if (physicalBody != null) {
	    physicalBody.addUser(this);
	}
	
	if (!VirtualUniverse.mc.isRegistered(this)) {
	    universe.regViewWaiting = this;
	}

	VirtualUniverse.mc.postRequest(MasterControl.ACTIVATE_VIEW,
				       this);

	if (!universe.isSceneGraphLock) {
	    universe.waitForMC();
	}
        if (soundScheduler != null) {
            soundScheduler.reset();
        }	

	J3dMessage vpMessage = new J3dMessage();
	vpMessage.universe = universe;
	vpMessage.view = this;
	vpMessage.type = J3dMessage.UPDATE_VIEW;
	vpMessage.threads = 
			    J3dThread.SOUND_SCHEDULER |
			    J3dThread.UPDATE_RENDER |
			    J3dThread.UPDATE_BEHAVIOR;
	vpMessage.args[0] = this;
	synchronized(((ViewPlatformRetained)viewPlatform.retained).sphere) {
	    vpMessage.args[1] = new Float(((ViewPlatformRetained)viewPlatform.retained).sphere.radius);
	}
	vpMessage.args[2] = new Integer(OTHER_ATTRS_CHANGED);
	vpMessage.args[3] = new Integer(transparencySortingPolicy);
	VirtualUniverse.mc.processMessage(vpMessage);
    }

    /**
     * This deactivates all traversers and renderers associated with this view.
     */
    void deactivate() {
	VirtualUniverse.mc.postRequest(MasterControl.DEACTIVATE_VIEW, this);
	if (physicalBody != null) {
	    physicalBody.removeUser(this);
	}

	// This is a temporary fix for bug 4267395 
	// XXXX:cleanup in RenderBin after View detach
	// universe.addViewIdToFreeList(viewId);
	
	// using new property -Dj3d.forceReleaseView to disable bug fix 4267395
	// this bug fix can produce memory leaks in *some* applications which creates 
	// and destroy Canvas3D from time to time. This just add the view in the 
	// FreeList earlier.
	if (VirtualUniverse.mc.forceReleaseView) {
	    universe.addViewIdToFreeList(viewId);
	}


	J3dMessage vpMessage = new J3dMessage();
	vpMessage.universe = universe;
	vpMessage.view = this;
	vpMessage.type = J3dMessage.UPDATE_VIEW;
	vpMessage.threads = 
			    J3dThread.SOUND_SCHEDULER |
			    J3dThread.UPDATE_RENDER |
			    J3dThread.UPDATE_BEHAVIOR;
	vpMessage.args[0] = this;
	if (viewPlatform != null) {
	    synchronized(((ViewPlatformRetained)viewPlatform.retained).sphere) {
		vpMessage.args[1] = new Float(((ViewPlatformRetained)viewPlatform.retained).sphere.radius);
	    }
	} else {
	    vpMessage.args[1] = new Float(0);
	}
	vpMessage.args[2] = new Integer(OTHER_ATTRS_CHANGED);
	vpMessage.args[3] = new Integer(transparencySortingPolicy);
	VirtualUniverse.mc.processMessage(vpMessage);

    }

    void cleanupViewId() {
	universe.addViewIdToFreeList(viewId);
	viewId = null;
    }


    void assignViewId () {
	if (viewId == null) {
	    viewId = universe.getViewId();
	    viewIndex = viewId.intValue();
	}
    }
    
    /**
     * This method passes window event to SoundScheduler
     */  
    void sendEventToSoundScheduler(AWTEvent evt) {
        if (soundScheduler != null) {
            soundScheduler.receiveAWTEvent(evt);
        }
    }

    void reset() {

	for (int i=0; i < canvases.size(); i++) {
	    ((Canvas3D) canvases.get(i)).reset();
	}

        // reset the renderBinReady flag
        renderBinReady = false;

	soundScheduler.cleanup();
	soundScheduler = null;
	soundRenderer = new SoundRenderer();
	
	viewCache = new ViewCache(this);
	getCanvasList(true);
	cleanupViewId();
	renderBin.cleanup();
	renderBin = null;
	universe = null;
    }
}
