Java tutorial
/* * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. * */ package javax.media.j3d; import java.awt.AWTEvent; import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; import java.util.LinkedList; import java.util.Vector; import javax.vecmath.Point3d; import javax.vecmath.Point3f; /** * 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<Canvas3D> canvases = new Vector<Canvas3D>(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; // 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; // 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<Screen3D> screenList = new ArrayList<Screen3D>(); // use getCanvasList() to access this private ArrayList<ArrayList<Canvas3D>> canvasList = new ArrayList<ArrayList<Canvas3D>>(); 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 = 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 = 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 = 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) { 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) { 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 = 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 this.canvases.elementAt(index); } /** * Gets the enumeration object of all the Canvas3Ds. * @return the enumeration object of all the Canvas3Ds. */ public Enumeration<Canvas3D> 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 = 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<Canvas3D> tmpCanvases = new LinkedList<Canvas3D>(); synchronized (canvasList) { int numCanvases = canvases.size(); // Remove in reverse order to ensure valid indices for (int index = numCanvases - 1; index >= 0; index--) { Canvas3D cv = 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<Canvas3D> iterator = tmpCanvases.iterator(); while (iterator.hasNext()) { Canvas3D cv = 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 (screenList.get(i) == c.screen) { // This is the right screen slot canvasList.get(i).add(c); canvasesDirty = true; return; } } // Add a screen slot screenList.add(c.screen); ArrayList<Canvas3D> clist = new ArrayList<Canvas3D>(); 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 (screenList.get(i) == c.screen) { // This is the right screen slot ArrayList<Canvas3D> clist = 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<Canvas3D> cv; int len = canvases.size(); Canvas3D newCachedCanvases[] = new Canvas3D[len]; for (int i = 0; i < len; i++) { newCachedCanvases[i] = 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 = canvasList.get(i); len = cv.size(); cachedCanvasList[i] = new Canvas3D[len]; for (int j = 0; j < len; j++) { cachedCanvasList[i][j] = 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] = 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 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 (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 = 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() { 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 = 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 = 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++) { canvases.get(i).reset(); } // reset the renderBinReady flag renderBinReady = false; soundScheduler.cleanup(); soundScheduler = null; viewCache = new ViewCache(this); getCanvasList(true); cleanupViewId(); renderBin.cleanup(); renderBin = null; universe = null; } }