Java tutorial
/* * Copyright 1997-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.awt.Canvas; import java.awt.Container; import java.awt.Dimension; import java.awt.Frame; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.IllegalComponentStateException; import java.awt.Point; import java.awt.Window; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; import javax.vecmath.Color3f; import javax.vecmath.Point2d; import javax.vecmath.Point3d; import javax.vecmath.Vector4d; /** * The Canvas3D class provides a drawing canvas for 3D rendering. It * is used either for on-screen rendering or off-screen rendering. * Canvas3D is an extension of the AWT Canvas class that users may * further subclass to implement additional functionality. * <p> * The Canvas3D object extends the Canvas object to include * 3D-related information such as the size of the canvas in pixels, * the Canvas3D's location, also in pixels, within a Screen3D object, * and whether or not the canvas has stereo enabled. * <p> * Because all Canvas3D objects contain a * reference to a Screen3D object and because Screen3D objects define * the size of a pixel in physical units, Java 3D can convert a Canvas3D * size in pixels to a physical world size in meters. It can also * determine the Canvas3D's position and orientation in the * physical world. * <p> * <b>On-screen Rendering vs. Off-screen Rendering</b> * <p> * The Canvas3D class is used either for on-screen rendering or * off-screen rendering. * On-screen Canvas3Ds are added to AWT or Swing Container objects * like any other canvas. Java 3D automatically and continuously * renders to all on-screen canvases that are attached to an active * View object. On-screen Canvas3Ds can be either single or double * buffered and they can be either stereo or monoscopic. * <p> * Off-screen Canvas3Ds must not be added to any Container. Java 3D * renders to off-screen canvases in response to the * <code>renderOffScreenBuffer</code> method. Off-screen Canvas3Ds * are single buffered. However, on many systems, the actual * rendering is done to an off-screen hardware buffer or to a 3D * library-specific buffer and only copied to the off-screen buffer of * the Canvas when the rendering is complete, at "buffer swap" time. * Off-screen Canvas3Ds are monoscopic. * <p> * The setOffScreenBuffer method sets the off-screen buffer for this * Canvas3D. The specified image is written into by the Java 3D renderer. * The size of the specified ImageComponent determines the size, in * pixels, of this Canvas3D - the size inherited from Component is * ignored. Note that the size, physical width, and physical height of the * associated Screen3D must be set * explicitly prior to rendering. Failure to do so will result in an * exception. * <p> * The getOffScreenBuffer method retrieves the off-screen * buffer for this Canvas3D. * <p> * The renderOffScreenBuffer method schedules the rendering of a frame * into this Canvas3D's off-screen buffer. The rendering is done from * the point of view of the View object to which this Canvas3D has been * added. No rendering is performed if this Canvas3D object has not been * added to an active View. This method does not wait for the rendering * to actually happen. An application that wishes to know when the * rendering is complete must either subclass Canvas3D and * override the postSwap method, or call waitForOffScreenRendering. * <p> * The setOfScreenLocation methods set the location of this off-screen * Canvas3D. The location is the upper-left corner of the Canvas3D * relative to the upper-left corner of the corresponding off-screen * Screen3D. The function of these methods is similar to that of * Component.setLocation for on-screen Canvas3D objects. The default * location is (0,0). * <p> * <b>Accessing and Modifying an Eye's Image Plate Position</b> * <p> * A Canvas3D object provides sophisticated applications with access * to the eye's position information in head-tracked, room-mounted * runtime environments. It also allows applications to manipulate * the position of an eye relative to an image plate in non-head-tracked * runtime environments. * <p> * The setLeftManualEyeInImagePlate and setRightManualEyeInImagePlate * methods set the position of the manual left and right eyes in image * plate coordinates. These values determine eye placement when a head * tracker is not in use and the application is directly controlling the * eye position in image plate coordinates. In head-tracked mode or * when the windowEyepointPolicy is RELATIVE_TO_FIELD_OF_VIEW or * RELATIVE_TO_COEXISTENCE, this * value is ignored. When the windowEyepointPolicy is RELATIVE_TO_WINDOW, * only the Z value is used. * <p> * The getLeftEyeInImagePlate, getRightEyeInImagePlate, and * getCenterEyeInImagePlate methods retrieve the actual position of the * left eye, right eye, and center eye in image plate coordinates and * copy that value into the object provided. The center eye is the * fictional eye half-way between the left and right eye. These three * values are a function of the windowEyepointPolicy, the tracking * enable flag, and the manual left, right, and center eye positions. * <p> * <b>Monoscopic View Policy</b> * <p> * The setMonoscopicViewPolicy and getMonoscopicViewPolicy methods * set and retrieve the policy regarding how Java 3D generates monoscopic * view. If the policy is set to View.LEFT_EYE_VIEW, the view generated * corresponds to the view as seen from the left eye. If set to * View.RIGHT_EYE_VIEW, the view generated corresponds to the view as * seen from the right eye. If set to View.CYCLOPEAN_EYE_VIEW, the view * generated corresponds to the view as seen from the "center eye," the * fictional eye half-way between the left and right eye. The default * monoscopic view policy is View.CYCLOPEAN_EYE_VIEW. * <p> * <b>Immediate Mode Rendering</b> * <p> * Pure immediate-mode rendering provides for those applications and * applets that do not want Java 3D to do any automatic rendering of * the scene graph. Such applications may not even wish to build a * scene graph to represent their graphical data. However, they use * Java 3D's attribute objects to set graphics state and Java 3D's * geometric objects to render geometry. * <p> * A pure immediate mode application must create a minimal set of * Java 3D objects before rendering. In addition to a Canvas3D object, * the application must create a View object, with its associated * PhysicalBody and PhysicalEnvironment objects, and the following * scene graph elements: a VirtualUniverse object, a high-resolution * Locale object, a BranchGroup node object, a TransformGroup node * object with associated transform, and a ViewPlatform * leaf node object that defines the position and orientation within * the virtual universe that generates the view. * <p> * In immediate mode, all rendering is done completely under user * control. It is necessary for the user to clear the 3D canvas, * render all geometry, and swap the buffers. Additionally, * rendering the right and left eye for stereo viewing becomes the * sole responsibility of the application. In pure immediate mode, * the user must stop the Java 3D renderer, via the * Canvas3D object <code>stopRenderer</code> method, prior to adding the * Canvas3D object to an active View object (that is, one that is * attached to a live ViewPlatform object). * <p> * Other Canvas3D methods related to immediate mode rendering are: * <p> * <ul> * <code>getGraphicsContext3D</code> retrieves the immediate-mode * 3D graphics context associated with this Canvas3D. It creates a * new graphics context if one does not already exist. * <p> * <code>getGraphics2D</code> retrieves the * 2D graphics object associated with this Canvas3D. It creates a * new 2D graphics object if one does not already exist. * <p> * <code>swap</code> synchronizes and swaps buffers on a * double-buffered canvas for this Canvas3D object. This method * should only be called if the Java 3D renderer has been stopped. * In the normal case, the renderer automatically swaps * the buffer. * </ul> * * <p> * <b>Mixed Mode Rendering</b> * <p> * Mixing immediate mode and retained or compiled-retained mode * requires more structure than pure immediate mode. In mixed mode, * the Java 3D renderer is running continuously, rendering the scene * graph into the canvas. * * <p> * Canvas3D methods related to mixed mode rendering are: * * <p> * <ul> * <code>preRender</code> called by the Java 3D rendering loop after * clearing the canvas and before any rendering has been done for * this frame. * <p> * <code>postRender</code> called by the Java 3D rendering loop after * completing all rendering to the canvas for this frame and before * the buffer swap. * <p> * <code>postSwap</code> called by the Java 3D rendering loop after * completing all rendering to the canvas, and all other canvases * associated with this view, for this frame following the * buffer swap. * <p> * <code>renderField</code> called by the Java 3D rendering loop * during the execution of the rendering loop. It is called once * for each field (i.e., once per frame on a mono system or once * each for the right eye and left eye on a two-pass stereo system. * </ul> * <p> * The above callback methods are called by the Java 3D rendering system * and should <i>not</i> be called by an application directly. * * <p> * The basic Java 3D <i>stereo</i> rendering loop, * executed for each Canvas3D, is as follows: * <ul><pre> * clear canvas (both eyes) * call preRender() // user-supplied method * set left eye view * render opaque scene graph objects * call renderField(FIELD_LEFT) // user-supplied method * render transparent scene graph objects * set right eye view * render opaque scene graph objects again * call renderField(FIELD_RIGHT) // user-supplied method * render transparent scene graph objects again * call postRender() // user-supplied method * synchronize and swap buffers * call postSwap() // user-supplied method * </pre></ul> * <p> * The basic Java 3D <i>monoscopic</i> rendering loop is as follows: * <ul><pre> * clear canvas * call preRender() // user-supplied method * set view * render opaque scene graph objects * call renderField(FIELD_ALL) // user-supplied method * render transparent scene graph objects * call postRender() // user-supplied method * synchronize and swap buffers * call postSwap() // user-supplied method * </pre></ul> * <p> * In both cases, the entire loop, beginning with clearing the canvas * and ending with swapping the buffers, defines a frame. The application * is given the opportunity to render immediate-mode geometry at any of * the clearly identified spots in the rendering loop. A user specifies * his or her own rendering methods by extending the Canvas3D class and * overriding the preRender, postRender, postSwap, and/or renderField * methods. * Updates to live Geometry, Texture, and ImageComponent objects * in the scene graph are not allowed from any of these callback * methods. * * <p> * <b>Serialization</b> * <p> * Canvas3D does <i>not</i> support serialization. An attempt to * serialize a Canvas3D object will result in an * UnsupportedOperationException being thrown. * * <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 Screen3D * @see View * @see GraphicsContext3D */ public class Canvas3D extends Canvas { /** * Specifies the left field of a field-sequential stereo rendering loop. * A left field always precedes a right field. */ public static final int FIELD_LEFT = 0; /** * Specifies the right field of a field-sequential stereo rendering loop. * A right field always follows a left field. */ public static final int FIELD_RIGHT = 1; /** * Specifies a single-field rendering loop. */ public static final int FIELD_ALL = 2; // // The following constants are bit masks to specify which of the node // components are dirty and need updates. // // Values for the geometryType field. static final int POLYGONATTRS_DIRTY = 0x01; static final int LINEATTRS_DIRTY = 0x02; static final int POINTATTRS_DIRTY = 0x04; static final int MATERIAL_DIRTY = 0x08; static final int TRANSPARENCYATTRS_DIRTY = 0x10; static final int COLORINGATTRS_DIRTY = 0x20; // Values for lightbin, env set, texture, texture setting etc. static final int LIGHTBIN_DIRTY = 0x40; static final int LIGHTENABLES_DIRTY = 0x80; static final int AMBIENTLIGHT_DIRTY = 0x100; static final int ATTRIBUTEBIN_DIRTY = 0x200; static final int TEXTUREBIN_DIRTY = 0x400; static final int TEXTUREATTRIBUTES_DIRTY = 0x800; static final int RENDERMOLECULE_DIRTY = 0x1000; static final int FOG_DIRTY = 0x2000; static final int MODELCLIP_DIRTY = 0x4000; static final int VIEW_MATRIX_DIRTY = 0x8000; // static final int SHADER_DIRTY = 0x10000; Not ready for this yet -- JADA // // Flag that indicates whether this Canvas3D is an off-screen Canvas3D // boolean offScreen = false; // // Issue 131: Flag that indicates whether this Canvas3D is a manually // rendered Canvas3D (versus an automatically rendered Canvas3D). // // NOTE: manualRendering only applies to off-screen Canvas3Ds at this time. // We have no plans to ever change this, but if we do, it might be necessary // to determine which, if any, of the uses of "manualRendering" should be // changed to "manualRendering&&offScreen" // boolean manualRendering = false; // user specified offScreen Canvas location Point offScreenCanvasLoc; // user specified offScreen Canvas dimension Dimension offScreenCanvasSize; // // Flag that indicates whether off-screen rendering is in progress or not // volatile boolean offScreenRendering = false; // // Flag that indicates we are waiting for an off-screen buffer to be // created or destroyed by the Renderer. // volatile boolean offScreenBufferPending = false; // // ImageComponent used for off-screen rendering // ImageComponent2D offScreenBuffer = null; // flag that indicates whether this canvas will use shared context boolean useSharedCtx = true; // // Read-only flag that indicates whether stereo is supported for this // canvas. This is always false for off-screen canvases. // boolean stereoAvailable; // // Flag to enable stereo rendering, if allowed by the // stereoAvailable flag. // boolean stereoEnable = true; // // This flag is set when stereo mode is both enabled and // available. Code that looks at stereo mode should use this // flag. // boolean useStereo; // Indicate whether it is left or right stereo pass currently boolean rightStereoPass = false; // // Specifies how Java 3D generates monoscopic view // (LEFT_EYE_VIEW, RIGHT_EYE_VIEW, or CYCLOPEAN_EYE_VIEW). // int monoscopicViewPolicy = View.CYCLOPEAN_EYE_VIEW; // User requested stencil size int requestedStencilSize; // Actual stencil size return for this canvas int actualStencilSize; // True if stencil buffer is available for user boolean userStencilAvailable; // True if stencil buffer is available for system ( decal ) boolean systemStencilAvailable; // // Read-only flag that indicates whether double buffering is supported // for this canvas. This is always false for off-screen canvases. // boolean doubleBufferAvailable; // // Flag to enable double buffered rendering, if allowed by the // doubleBufferAvailable flag. // boolean doubleBufferEnable = true; // // This flag is set when doubleBuffering is both enabled and // available Code that enables or disables double buffering should // use this flag. // boolean useDoubleBuffer; // // Read-only flag that indicates whether scene antialiasing // is supported for this canvas. // boolean sceneAntialiasingAvailable; boolean sceneAntialiasingMultiSamplesAvailable; // Use to see whether antialiasing is already set private boolean antialiasingSet = false; // // Read-only flag that indicates the size of the texture color // table for this canvas. A value of 0 indicates that the texture // color table is not supported. // int textureColorTableSize; // number of active/enabled texture unit int numActiveTexUnit = 0; // index iof last enabled texture unit int lastActiveTexUnit = -1; // True if shadingLanguage is supported, otherwise false. boolean shadingLanguageGLSL = false; // Query properties J3dQueryProps queryProps; // Flag indicating a fatal rendering error of some sort private boolean fatalError = false; // // The positions of the manual left and right eyes in image-plate // coordinates. // By default, we will use the center of the screen for X and Y values // (X values are adjusted for default eye separation), and // 0.4572 meters (18 inches) for the Z value. // These match defaults elsewhere in the system. // Point3d leftManualEyeInImagePlate = new Point3d(0.142, 0.135, 0.4572); Point3d rightManualEyeInImagePlate = new Point3d(0.208, 0.135, 0.4572); // // View that is attached to this Canvas3D. // View view = null; // View waiting to be set View pendingView; // // View cache for this canvas and its associated view. // CanvasViewCache canvasViewCache = null; // Issue 109: View cache for this canvas, for computing view frustum planes CanvasViewCache canvasViewCacheFrustum = null; // Since multiple renderAtomListInfo, share the same vecBounds // we want to do the intersection test only once per renderAtom // this flag is set to true after the first intersect and set to // false during checkForCompaction in renderBin boolean raIsVisible = false; RenderAtom ra = null; // Stereo related field has changed. static final int STEREO_DIRTY = 0x01; // MonoscopicViewPolicy field has changed. static final int MONOSCOPIC_VIEW_POLICY_DIRTY = 0x02; // Left/right eye in image plate field has changed. static final int EYE_IN_IMAGE_PLATE_DIRTY = 0x04; // Canvas has moved/resized. static final int MOVED_OR_RESIZED_DIRTY = 0x08; // Canvas Background changed (this may affect doInfinite flag) static final int BACKGROUND_DIRTY = 0x10; // Canvas Background Image changed static final int BACKGROUND_IMAGE_DIRTY = 0x20; // Mask that indicates this Canvas view dependence info. has changed, // and CanvasViewCache may need to recompute the final view matries. static final int VIEW_INFO_DIRTY = (STEREO_DIRTY | MONOSCOPIC_VIEW_POLICY_DIRTY | EYE_IN_IMAGE_PLATE_DIRTY | MOVED_OR_RESIZED_DIRTY | BACKGROUND_DIRTY | BACKGROUND_IMAGE_DIRTY); // Issue 163: Array of dirty bits is used because the Renderer and // RenderBin run asynchronously. Now that they each have a separate // instance of CanvasViewCache (due to the fix for Issue 109), they // need separate dirty bits. Array element 0 is used for the Renderer and // element 1 is used for the RenderBin. static final int RENDERER_DIRTY_IDX = 0; static final int RENDER_BIN_DIRTY_IDX = 1; int[] cvDirtyMask = new int[2]; // This boolean informs the J3DGraphics2DImpl that the window is resized boolean resizeGraphics2D = true; // // This boolean allows an application to start and stop the render // loop on this canvas. // volatile boolean isRunning = true; // This is used by MasterControl only. MC relay on this in a // single loop to set renderer thread. During this time, // the isRunningStatus can't change by user thread. volatile boolean isRunningStatus = true; // This is true when the canvas is ready to be rendered into boolean active = false; // This is true when the canvas is visible boolean visible = false; // This is true if context need to recreate boolean ctxReset = true; // The Screen3D that corresponds to this Canvas3D Screen3D screen = null; // Flag to indicate that image is render completely // so swap is valid. boolean imageReady = false; // The 3D Graphics context used for immediate mode rendering // into this canvas. GraphicsContext3D graphicsContext3D = null; boolean waiting = false; boolean swapDone = false; GraphicsConfiguration graphicsConfiguration; // The Java 3D Graphics2D object used for Java2D/AWT rendering // into this Canvas3D J3DGraphics2DImpl graphics2D = null; // Lock used to synchronize the creation of the 2D and 3D // graphics context objects Object gfxCreationLock = new Object(); // The source of the currently loaded localToVWorld for this Canvas3D // (used to only update the model matrix when it changes) // Transform3D localToVWorldSrc = null; // The current vworldToEc Transform Transform3D vworldToEc = new Transform3D(); // The view transform (VPC to EC) for the current eye. // NOTE that this is *read-only* Transform3D vpcToEc; // Opaque object representing the underlying drawable (window). This // is defined by the Pipeline. Drawable drawable = null; // graphicsConfigTable is a static hashtable which allows getBestConfiguration() // in NativeConfigTemplate3D to map a GraphicsConfiguration to the pointer // to the actual GLXFBConfig that glXChooseFBConfig() returns. The Canvas3D // doesn't exist at the time getBestConfiguration() is called, and // X11GraphicsConfig neither maintains this pointer nor provides a public // constructor to allow Java 3D to extend it. static Hashtable<GraphicsConfiguration, GraphicsConfigInfo> graphicsConfigTable = new Hashtable<GraphicsConfiguration, GraphicsConfigInfo>(); // The native graphics version, vendor, and renderer information String nativeGraphicsVersion = "<UNKNOWN>"; String nativeGraphicsVendor = "<UNKNOWN>"; String nativeGraphicsRenderer = "<UNKNOWN>"; boolean firstPaintCalled = false; // This reflects whether or not this canvas has seen an addNotify. It is // forced to true for off-screen canvases boolean added = false; // Flag indicating whether addNotify has been called (so we don't process it twice). private boolean addNotifyCalled = false; // This is the id for the underlying graphics context structure. Context ctx = null; // since the ctx id can be the same as the previous one, // we need to keep a time stamp to differentiate the contexts with the // same id volatile long ctxTimeStamp = 0; // The current context setting for local eye lighting boolean ctxEyeLightingEnable = false; // This AppearanceRetained Object refelects the current state of this // canvas. It is used to optimize setting of attributes at render time. AppearanceRetained currentAppear = new AppearanceRetained(); // This MaterialRetained Object refelects the current state of this canvas. // It is used to optimize setting of attributes at render time. MaterialRetained currentMaterial = new MaterialRetained(); /** * The object used for View Frustum Culling */ CachedFrustum viewFrustum = new CachedFrustum(); /** * The RenderBin bundle references used to decide what the underlying * context contains. */ LightBin lightBin = null; EnvironmentSet environmentSet = null; AttributeBin attributeBin = null; ShaderBin shaderBin = null; RenderMolecule renderMolecule = null; PolygonAttributesRetained polygonAttributes = null; LineAttributesRetained lineAttributes = null; PointAttributesRetained pointAttributes = null; MaterialRetained material = null; boolean enableLighting = false; TransparencyAttributesRetained transparency = null; ColoringAttributesRetained coloringAttributes = null; Transform3D modelMatrix = null; Transform3D projTrans = null; TextureBin textureBin = null; /** * cached RenderBin states for lazy native states update */ LightRetained lights[] = null; int frameCount[] = null; long enableMask = -1; FogRetained fog = null; ModelClipRetained modelClip = null; Color3f sceneAmbient = new Color3f(); TextureUnitStateRetained[] texUnitState = null; /** * These cached values are only used in Pure Immediate and Mixed Mode rendering */ TextureRetained texture = null; TextureAttributesRetained texAttrs = null; TexCoordGenerationRetained texCoordGeneration = null; RenderingAttributesRetained renderingAttrs = null; AppearanceRetained appearance = null; ShaderProgramRetained shaderProgram = null; // only used in Mixed Mode rendering Object appHandle = null; /** * Dirty bit to determine if the NodeComponent needs to be re-sent * down to the underlying API */ int canvasDirty = 0xffff; // True when either one of dirtyRenderMoleculeList, // dirtyDlistPerRinfoList, dirtyRenderAtomList size > 0 boolean dirtyDisplayList = false; ArrayList<RenderMolecule> dirtyRenderMoleculeList = new ArrayList<RenderMolecule>(); ArrayList<RenderAtomListInfo> dirtyRenderAtomList = new ArrayList<RenderAtomListInfo>(); // List of (Rm, rInfo) pair of individual dlists that need to be rebuilt ArrayList<Object[]> dirtyDlistPerRinfoList = new ArrayList<Object[]>(); ArrayList<Integer> displayListResourceFreeList = new ArrayList<Integer>(); ArrayList<Integer> textureIdResourceFreeList = new ArrayList<Integer>(); // an unique bit to identify this canvas int canvasBit = 0; // an unique number to identify this canvas : ( canvasBit = 1 << canvasId) int canvasId = 0; // Indicates whether the canvasId has been allocated private boolean canvasIdAlloc = false; // Avoid using this as lock, it cause deadlock Object cvLock = new Object(); Object evaluateLock = new Object(); Object dirtyMaskLock = new Object(); // For D3D, instead of using the same variable in Renderer, // each canvas has to build its own displayList. boolean needToRebuildDisplayList = false; // Read-only flag that indicates whether the following texture features // are supported for this canvas. static final int TEXTURE_3D = 0x0001; static final int TEXTURE_COLOR_TABLE = 0x0002; static final int TEXTURE_MULTI_TEXTURE = 0x0004; static final int TEXTURE_COMBINE = 0x0008; static final int TEXTURE_COMBINE_DOT3 = 0x0010; static final int TEXTURE_COMBINE_SUBTRACT = 0x0020; static final int TEXTURE_REGISTER_COMBINERS = 0x0040; static final int TEXTURE_CUBE_MAP = 0x0080; static final int TEXTURE_SHARPEN = 0x0100; static final int TEXTURE_DETAIL = 0x0200; static final int TEXTURE_FILTER4 = 0x0400; static final int TEXTURE_ANISOTROPIC_FILTER = 0x0800; static final int TEXTURE_LOD_RANGE = 0x1000; static final int TEXTURE_LOD_OFFSET = 0x2000; // Use by D3D to indicate using one pass Blend mode // if Texture interpolation mode is support. static final int TEXTURE_LERP = 0x4000; static final int TEXTURE_NON_POWER_OF_TWO = 0x8000; static final int TEXTURE_AUTO_MIPMAP_GENERATION = 0x10000; int textureExtendedFeatures = 0; // Extensions supported by the underlying canvas // // NOTE: we should remove EXT_BGR and EXT_ABGR when the imaging code is // rewritten // killed global alpha //static final int SUN_GLOBAL_ALPHA = 0x1; static final int EXT_ABGR = 0x2; static final int EXT_BGR = 0x4; static final int MULTISAMPLE = 0x8; // The following 10 variables are set by the native // createNewContext()/createQueryContext() methods // Supported Extensions int extensionsSupported = 0; // Anisotropic Filter degree float anisotropicDegreeMax = 1.0f; // Texture Boundary Width Max int textureBoundaryWidthMax = 0; boolean multiTexAccelerated = false; // Max number of texture coordinate sets int maxTexCoordSets = 1; // Max number of fixed-function texture units int maxTextureUnits = 1; // Max number of fragment shader texture units int maxTextureImageUnits = 0; // Max number of vertex shader texture units int maxVertexTextureImageUnits = 0; // Max number of combined shader texture units int maxCombinedTextureImageUnits = 0; // Max number of vertex attrs (not counting coord, etc.) int maxVertexAttrs = 0; // End of variables set by createNewContext()/createQueryContext() // The total available number of texture units used by either the // fixed-function or programmable shader pipeline. // This is computed as: max(maxTextureUnits, maxTextureImageUnits) int maxAvailableTextureUnits; // Texture Width, Height Max int textureWidthMax = 0; int textureHeightMax = 0; // Texture3D Width, Heigh, Depth Max int texture3DWidthMax = -1; int texture3DHeightMax = -1; int texture3DDepthMax = -1; // Cached position & size for CanvasViewCache. // We don't want to call canvas.getxx method in Renderer // since it will cause deadlock as removeNotify() need to get // component lock of Canvas also and need to wait Renderer to // finish before continue. So we invoke the method now in // CanvasViewEventCatcher. Point newPosition = new Point(); Dimension newSize = new Dimension(); double xscale = 1.0; double yscale = 1.0; // Remember OGL context resources to free // before context is destroy. // It is used when sharedCtx = false; ArrayList<TextureRetained> textureIDResourceTable = new ArrayList<TextureRetained>(5); // The following variables are used by the lazy download of // states code to keep track of the set of current to be update bins static final int LIGHTBIN_BIT = 0x0; static final int ENVIRONMENTSET_BIT = 0x1; static final int ATTRIBUTEBIN_BIT = 0x2; static final int TEXTUREBIN_BIT = 0x3; static final int RENDERMOLECULE_BIT = 0x4; static final int TRANSPARENCY_BIT = 0x5; static final int SHADERBIN_BIT = 0x6; // bitmask to specify if the corresponding "bin" needs to be updated int stateUpdateMask = 0; // the set of current "bins" that is to be updated, the stateUpdateMask // specifies if each bin in this set is updated or not. Object curStateToUpdate[] = new Object[7]; /** * The list of lights that are currently being represented in the native * graphics context. */ LightRetained[] currentLights = null; /** * Flag to override RenderAttributes.depthBufferWriteEnable */ boolean depthBufferWriteEnableOverride = false; /** * Flag to override RenderAttributes.depthBufferEnable */ boolean depthBufferEnableOverride = false; // current state of depthBufferWriteEnable boolean depthBufferWriteEnable = true; boolean vfPlanesValid = false; // The event catcher for this canvas. EventCatcher eventCatcher; // The view event catcher for this canvas. private CanvasViewEventCatcher canvasViewEventCatcher; // The top-level parent window for this canvas. private Window windowParent; // Issue 458 - list of all parent containers for this canvas // (includes top-level parent window) private LinkedList<Container> containerParentList = new LinkedList<Container>(); // flag that indicates if light has changed boolean lightChanged = false; // resource control object DrawingSurfaceObject drawingSurfaceObject; // true if context is valid for rendering boolean validCtx = false; // true if canvas is valid for rendering boolean validCanvas = false; // true if ctx changed between render and swap. In this case // cv.canvasDirty flag will not reset in Renderer. // This case happen when GraphicsContext3D invoked doClear() // and canvas removeNotify() called while Renderer is running boolean ctxChanged = false; // Default graphics configuration private static GraphicsConfiguration defaultGcfg = null; // Returns default graphics configuration if user passes null // into the Canvas3D constructor private static synchronized GraphicsConfiguration defaultGraphicsConfiguration() { if (defaultGcfg == null) { GraphicsConfigTemplate3D template = new GraphicsConfigTemplate3D(); defaultGcfg = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice() .getBestConfiguration(template); } return defaultGcfg; } // Returns true if this is a valid graphics configuration, obtained // via a GraphicsConfigTemplate3D. private static boolean isValidConfig(GraphicsConfiguration gconfig) { // If this is a valid GraphicsConfiguration object, then it will // be in the graphicsConfigTable return graphicsConfigTable.containsKey(gconfig); } // Checks the given graphics configuration, and throws an exception if // the config is null or invalid. private static synchronized GraphicsConfiguration checkForValidGraphicsConfig(GraphicsConfiguration gconfig, boolean offScreen) { // Issue 266 - for backwards compatibility with legacy applications, // we will accept a null GraphicsConfiguration for an on-screen Canvas3D // only if the "allowNullGraphicsConfig" system property is set to true. if (!offScreen && VirtualUniverse.mc.allowNullGraphicsConfig) { if (gconfig == null) { // Print out warning if Canvas3D is called with a // null GraphicsConfiguration System.err.println(J3dI18N.getString("Canvas3D7")); System.err.println(" " + J3dI18N.getString("Canvas3D18")); // Use a default graphics config gconfig = defaultGraphicsConfiguration(); } } // Validate input graphics config if (gconfig == null) { throw new NullPointerException(J3dI18N.getString("Canvas3D19")); } else if (!isValidConfig(gconfig)) { throw new IllegalArgumentException(J3dI18N.getString("Canvas3D17")); } return gconfig; } // Return the actual graphics config that will be used to construct // the AWT Canvas. This is permitted to be non-unique or null. private static GraphicsConfiguration getGraphicsConfig(GraphicsConfiguration gconfig) { return Pipeline.getPipeline().getGraphicsConfig(gconfig); } /** * Constructs and initializes a new Canvas3D object that Java 3D * can render into. The following Canvas3D attributes are initialized * to default values as shown: * <ul> * left manual eye in image plate : (0.142, 0.135, 0.4572)<br> * right manual eye in image plate : (0.208, 0.135, 0.4572)<br> * stereo enable : true<br> * double buffer enable : true<br> * monoscopic view policy : View.CYCLOPEAN_EYE_VIEW<br> * off-screen mode : false<br> * off-screen buffer : null<br> * off-screen location : (0,0)<br> * </ul> * * @param graphicsConfiguration a valid GraphicsConfiguration object that * will be used to create the canvas. This object should not be null and * should be created using a GraphicsConfigTemplate3D or the * getPreferredConfiguration() method of the SimpleUniverse utility. For * backward compatibility with earlier versions of Java 3D, a null or * default GraphicsConfiguration will still work when used to create a * Canvas3D on the default screen, but an error message will be printed. * A NullPointerException or IllegalArgumentException will be thrown in a * subsequent release. * * @exception IllegalArgumentException if the specified * GraphicsConfiguration does not support 3D rendering */ public Canvas3D(GraphicsConfiguration graphicsConfiguration) { this(null, checkForValidGraphicsConfig(graphicsConfiguration, false), false); } /** * Constructs and initializes a new Canvas3D object that Java 3D * can render into. * * @param graphicsConfiguration a valid GraphicsConfiguration object * that will be used to create the canvas. This must be created either * with a GraphicsConfigTemplate3D or by using the * getPreferredConfiguration() method of the SimpleUniverse utility. * * @param offScreen a flag that indicates whether this canvas is * an off-screen 3D rendering canvas. Note that if offScreen * is set to true, this Canvas3D object cannot be used for normal * rendering; it should not be added to any Container object. * * @exception NullPointerException if the GraphicsConfiguration * is null. * * @exception IllegalArgumentException if the specified * GraphicsConfiguration does not support 3D rendering * * @since Java 3D 1.2 */ public Canvas3D(GraphicsConfiguration graphicsConfiguration, boolean offScreen) { this(null, checkForValidGraphicsConfig(graphicsConfiguration, offScreen), offScreen); } // Private constructor only called by the two public constructors after // they have validated the graphics config (and possibly constructed a new // default config). // The graphics config must be valid, unique, and non-null. private Canvas3D(Object dummyObj1, GraphicsConfiguration graphicsConfiguration, boolean offScreen) { this(dummyObj1, graphicsConfiguration, getGraphicsConfig(graphicsConfiguration), offScreen); } // Private constructor only called by the previous private constructor. // The graphicsConfiguration parameter is used by Canvas3D to lookup the // graphics device and graphics template. The graphicsConfiguration2 // parameter is generated by the Pipeline from graphicsConfiguration and // is only used to initialize the java.awt.Canvas. private Canvas3D(Object dummyObj1, GraphicsConfiguration graphicsConfiguration, GraphicsConfiguration graphicsConfiguration2, boolean offScreen) { super(graphicsConfiguration2); this.offScreen = offScreen; this.graphicsConfiguration = graphicsConfiguration; // Issue 131: Set the autoOffScreen variable based on whether this // canvas3d implements the AutoOffScreenCanvas3D tagging interface. // Eventually, we may replace this with an actual API. boolean autoOffScreenCanvas3D = false; if (this instanceof AutoOffScreenCanvas3D) { autoOffScreenCanvas3D = true; } // Throw an illegal argument exception if an on-screen canvas is tagged // as an auto-off-screen canvas if (autoOffScreenCanvas3D && !offScreen) { throw new IllegalArgumentException(J3dI18N.getString("Canvas3D25")); } // Issue 163 : Set dirty bits for both Renderer and RenderBin cvDirtyMask[0] = VIEW_INFO_DIRTY; cvDirtyMask[1] = VIEW_INFO_DIRTY; GraphicsConfigInfo gcInfo = graphicsConfigTable.get(graphicsConfiguration); requestedStencilSize = gcInfo.getGraphicsConfigTemplate3D().getStencilSize(); if (offScreen) { // Issue 131: set manual rendering flag based on whether this is // an auto-off-screen Canvas3D. manualRendering = !autoOffScreenCanvas3D; screen = new Screen3D(graphicsConfiguration, offScreen); // QUESTION: keep a list of off-screen Screen3D objects? // Does this list need to be grouped by GraphicsDevice? synchronized (dirtyMaskLock) { cvDirtyMask[0] |= MOVED_OR_RESIZED_DIRTY; cvDirtyMask[1] |= MOVED_OR_RESIZED_DIRTY; } // this canvas will not receive the paint callback, // so we need to set the necessary flags here firstPaintCalled = true; if (manualRendering) { // since this canvas will not receive the addNotify // callback from AWT, set the added flag here for // evaluateActive to work correctly added = true; } evaluateActive(); // create the rendererStructure object //rendererStructure = new RendererStructure(); offScreenCanvasLoc = new Point(0, 0); offScreenCanvasSize = new Dimension(0, 0); this.setLocation(offScreenCanvasLoc); this.setSize(offScreenCanvasSize); newSize = offScreenCanvasSize; newPosition = offScreenCanvasLoc; // Issue 131: create event catchers for auto-offScreen if (!manualRendering) { eventCatcher = new EventCatcher(this); canvasViewEventCatcher = new CanvasViewEventCatcher(this); } } else { GraphicsDevice graphicsDevice; graphicsDevice = graphicsConfiguration.getDevice(); eventCatcher = new EventCatcher(this); canvasViewEventCatcher = new CanvasViewEventCatcher(this); synchronized (VirtualUniverse.mc.deviceScreenMap) { screen = VirtualUniverse.mc.deviceScreenMap.get(graphicsDevice); if (screen == null) { screen = new Screen3D(graphicsConfiguration, offScreen); VirtualUniverse.mc.deviceScreenMap.put(graphicsDevice, screen); } } } lights = new LightRetained[VirtualUniverse.mc.maxLights]; frameCount = new int[VirtualUniverse.mc.maxLights]; for (int i = 0; i < frameCount.length; i++) { frameCount[i] = -1; } // Construct the drawing surface object for this Canvas3D drawingSurfaceObject = Pipeline.getPipeline().createDrawingSurfaceObject(this); // Get double buffer, stereo available, scene antialiasing // flags from graphics config GraphicsConfigTemplate3D.getGraphicsConfigFeatures(this); useDoubleBuffer = doubleBufferEnable && doubleBufferAvailable; useStereo = stereoEnable && stereoAvailable; useSharedCtx = VirtualUniverse.mc.isSharedCtx; // Issue 131: assert that only an off-screen canvas can be demand-driven assert (!offScreen && manualRendering) == false; // Assert that offScreen is *not* stereo assert (offScreen && useStereo) == false; } /** * This method overrides AWT's handleEvent class... */ void sendEventToBehaviorScheduler(AWTEvent evt) { ViewPlatform vp; if ((view != null) && ((vp = view.getViewPlatform()) != null)) { VirtualUniverse univ = ((ViewPlatformRetained) (vp.retained)).universe; if (univ != null) { univ.behaviorStructure.handleAWTEvent(evt); } } } /** * Method to return whether or not the Canvas3D is recursively visible; * that is, whether the Canas3D is currently visible on the screen. Note * that we don't directly use isShowing() because that won't work for an * auto-offScreen Canvas3D. */ private boolean isRecursivelyVisible() { Container parent = getParent(); return isVisible() && parent != null && parent.isShowing(); } /** * Method to return whether the top-level Window parent is iconified */ private boolean isIconified() { if (windowParent instanceof Frame) { return (((Frame) windowParent).getExtendedState() & Frame.ICONIFIED) != 0; } return false; } // Issue 458 - evaluate this Canvas3D's visibility whenever we get a // Window or Component Event that could change it. void evaluateVisiblilty() { boolean nowVisible = isRecursivelyVisible() && !isIconified(); // Only need to reevaluate and repaint if visibility has changed if (this.visible != nowVisible) { this.visible = nowVisible; evaluateActive(); if (nowVisible) { if (view != null) { view.repaint(); } } } } /** * This version looks for the view and notifies it. */ void redraw() { if ((view != null) && active && isRunning) { view.repaint(); } } /** * Canvas3D uses the paint callback to track when it is possible to * render into the canvas. Subclasses of Canvas3D that override this * method need to call super.paint() in their paint method for Java 3D * to function properly. * @param g the graphics context */ @Override public void paint(Graphics g) { if (!firstPaintCalled && added && validCanvas && validGraphicsMode()) { final Graphics2D g2d = (Graphics2D) g; final AffineTransform t = g2d.getTransform(); try { Dimension scaledSize = getSize(); xscale = t.getScaleX(); yscale = t.getScaleY(); newSize = new Dimension((int) (scaledSize.getWidth() * xscale), (int) (scaledSize.getHeight() * yscale)); newPosition = getLocationOnScreen(); } catch (IllegalComponentStateException e) { return; } synchronized (drawingSurfaceObject) { drawingSurfaceObject.getDrawingSurfaceObjectInfo(); } firstPaintCalled = true; visible = true; evaluateActive(); } redraw(); } // When this canvas is added to a frame, this notification gets called. We // can get drawing surface information at this time. Note: we cannot get // the X11 window id yet, unless it is a reset condition. /** * Canvas3D uses the addNotify callback to track when it is added * to a container. Subclasses of Canvas3D that override this * method need to call super.addNotify() in their addNotify() method for Java 3D * to function properly. */ @Override public void addNotify() { // Return immediately if addNotify called twice with no removeNotify if (addNotifyCalled) { return; } addNotifyCalled = true; // Issue 131: This method is now being called by JCanvas3D for its // off-screen Canvas3D, so we need to handle off-screen properly here. // Do nothing for manually-rendered off-screen canvases if (manualRendering) { return; } Renderer rdr = null; if (isRunning && (screen != null)) { // If there is other Canvas3D in the same screen // rendering, stop it before JDK create new Canvas rdr = screen.renderer; if (rdr != null) { VirtualUniverse.mc.postRequest(MasterControl.STOP_RENDERER, rdr); while (!rdr.userStop) { MasterControl.threadYield(); } } } // Issue 131: Don't call super for off-screen Canvas3D if (!offScreen) { super.addNotify(); } screen.addUser(this); // Issue 458 - Add the eventCatcher as a component listener for each // parent container in the window hierarchy assert containerParentList.isEmpty(); windowParent = null; Container container = this.getParent(); while (container != null) { if (container instanceof Window) { windowParent = (Window) container; } container.addComponentListener(eventCatcher); container.addComponentListener(canvasViewEventCatcher); containerParentList.add(container); container = container.getParent(); } this.addComponentListener(eventCatcher); this.addComponentListener(canvasViewEventCatcher); if (windowParent != null) { windowParent.addWindowListener(eventCatcher); } synchronized (dirtyMaskLock) { cvDirtyMask[0] |= MOVED_OR_RESIZED_DIRTY; cvDirtyMask[1] |= MOVED_OR_RESIZED_DIRTY; } allocateCanvasId(); validCanvas = true; added = true; // Since we won't get a paint call for off-screen canvases, we need // to set the first paint and visible flags here. We also need to // call evaluateActive for the same reason. if (offScreen) { firstPaintCalled = true; visible = true; evaluateActive(); } // In case the same canvas is removed and add back, // we have to change isRunningStatus back to true; if (isRunning && !fatalError) { isRunningStatus = true; } ctxTimeStamp = 0; if ((view != null) && (view.universe != null)) { view.universe.checkForEnableEvents(); } if (rdr != null) { // Issue 84: Send a message to MC to restart renderer // Note that this also obviates the need for the earlier fix to // issue 131 which called redraw() for auto-off-screen Canvas3Ds // (and this is a more robust fix) VirtualUniverse.mc.postRequest(MasterControl.START_RENDERER, rdr); while (rdr.userStop) { MasterControl.threadYield(); } } } // When this canvas is removed a frame, this notification gets called. We // need to release the native context at this time. The underlying window // is about to go away. /** * Canvas3D uses the removeNotify callback to track when it is removed * from a container. Subclasses of Canvas3D that override this * method need to call super.removeNotify() in their removeNotify() * method for Java 3D to function properly. */ @Override public void removeNotify() { // Return immediately if addNotify not called first if (!addNotifyCalled) { return; } addNotifyCalled = false; // Do nothing for manually-rendered off-screen canvases if (manualRendering) { return; } Renderer rdr = null; if (isRunning && (screen != null)) { // If there is other Canvas3D in the same screen // rendering, stop it before JDK create new Canvas rdr = screen.renderer; if (rdr != null) { VirtualUniverse.mc.postRequest(MasterControl.STOP_RENDERER, rdr); while (!rdr.userStop) { MasterControl.threadYield(); } } } // Note that although renderer userStop is true, // MasterControl can still schedule renderer to run through // runMonotor(RUN_RENDERER_CLEANUP) which skip userStop // thread checking. // For non-offscreen rendering the following call will // block waiting until all resources is free before // continue synchronized (drawingSurfaceObject) { validCtx = false; validCanvas = false; } removeCtx(); Pipeline.getPipeline().freeDrawingSurface(this, drawingSurfaceObject); // Clear first paint and visible flags firstPaintCalled = false; visible = false; screen.removeUser(this); evaluateActive(); freeCanvasId(); ra = null; graphicsContext3D = null; ctx = null; // must be after removeCtx() because // it will free graphics2D textureID graphics2D = null; super.removeNotify(); // Release and clear. for (Container container : containerParentList) { container.removeComponentListener(eventCatcher); container.removeComponentListener(canvasViewEventCatcher); } containerParentList.clear(); this.removeComponentListener(eventCatcher); this.removeComponentListener(canvasViewEventCatcher); if (eventCatcher != null) { this.removeFocusListener(eventCatcher); this.removeKeyListener(eventCatcher); this.removeMouseListener(eventCatcher); this.removeMouseMotionListener(eventCatcher); this.removeMouseWheelListener(eventCatcher); eventCatcher.reset(); } if (windowParent != null) { windowParent.removeWindowListener(eventCatcher); windowParent.requestFocus(); } added = false; if (rdr != null) { // Issue 84: Send a message to MC to restart renderer VirtualUniverse.mc.postRequest(MasterControl.START_RENDERER, rdr); while (rdr.userStop) { MasterControl.threadYield(); } } // Fix for issue 102 removing strong reference and avoiding memory leak // due retention of parent container this.windowParent = null; } void allocateCanvasId() { if (!canvasIdAlloc) { canvasId = VirtualUniverse.mc.getCanvasId(); canvasBit = 1 << canvasId; canvasIdAlloc = true; } } void freeCanvasId() { if (canvasIdAlloc) { VirtualUniverse.mc.freeCanvasId(canvasId); canvasBit = 0; canvasId = 0; canvasIdAlloc = false; } } // This decides if the canvas is active void evaluateActive() { // Note that no need to check for isRunning, we want // view register in order to create scheduler in pure immedite mode // Also we can't use this as lock, otherwise there is a // deadlock where updateViewCache get a lock of this and // get a lock of this component. But Container // remove will get a lock of this component follows by evaluateActive. synchronized (evaluateLock) { if ((visible || manualRendering) && firstPaintCalled) { if (!active) { active = true; if (pendingView != null) { pendingView.evaluateActive(); } } else { if ((pendingView != null) && !pendingView.activeStatus) { pendingView.evaluateActive(); } } } else { if (active) { active = false; if (view != null) { view.evaluateActive(); } } } } if ((view != null) && (!active)) { VirtualUniverse u = view.universe; if ((u != null) && !u.isSceneGraphLock) { u.waitForMC(); } } } void setFrustumPlanes(Vector4d[] planes) { if (VirtualUniverse.mc.viewFrustumCulling) { /* System.err.println("Canvas3D.setFrustumPlanes()"); */ viewFrustum.set(planes); } } /** * Retrieve the Screen3D object that this Canvas3D is attached to. * If this Canvas3D is an off-screen buffer, a new Screen3D object * is created corresponding to the off-screen buffer. * @return the 3D screen object that this Canvas3D is attached to */ public Screen3D getScreen3D() { return screen; } /** * Get the immediate mode 3D graphics context associated with * this Canvas3D. A new graphics context object is created if one does * not already exist. * @return a GraphicsContext3D object that can be used for immediate * mode rendering to this Canvas3D. */ public GraphicsContext3D getGraphicsContext3D() { synchronized (gfxCreationLock) { if (graphicsContext3D == null) graphicsContext3D = new GraphicsContext3D(this); } return graphicsContext3D; } /** * Get the 2D graphics object associated with * this Canvas3D. A new 2D graphics object is created if one does * not already exist. * * @return a Graphics2D object that can be used for Java 2D * rendering into this Canvas3D. * * @since Java 3D 1.2 */ public J3DGraphics2D getGraphics2D() { synchronized (gfxCreationLock) { if (graphics2D == null) graphics2D = new J3DGraphics2DImpl(this); } return graphics2D; } /** * This routine is called by the Java 3D rendering loop after clearing * the canvas and before any rendering has been done for this frame. * Applications that wish to perform operations in the rendering loop, * prior to any actual rendering may override this function. * * <p> * Updates to live Geometry, Texture, and ImageComponent objects * in the scene graph are not allowed from this method. * * <p> * NOTE: Applications should <i>not</i> call this method. */ public void preRender() { // Do nothing; the user overrides this to cause some action } /** * This routine is called by the Java 3D rendering loop after completing * all rendering to the canvas for this frame and before the buffer swap. * Applications that wish to perform operations in the rendering loop, * following any actual rendering may override this function. * * <p> * Updates to live Geometry, Texture, and ImageComponent objects * in the scene graph are not allowed from this method. * * <p> * NOTE: Applications should <i>not</i> call this method. */ public void postRender() { // Do nothing; the user overrides this to cause some action } /** * This routine is called by the Java 3D rendering loop after completing * all rendering to the canvas, and all other canvases associated with * this view, for this frame following the buffer swap. * Applications that wish to perform operations at the very * end of the rendering loop may override this function. * In off-screen mode, all rendering is copied to the off-screen * buffer before this method is called. * * <p> * Updates to live Geometry, Texture, and ImageComponent objects * in the scene graph are not allowed from this method. * * <p> * NOTE: Applications should <i>not</i> call this method. */ public void postSwap() { // Do nothing; the user overrides this to cause some action } /** * This routine is called by the Java 3D rendering loop during the * execution of the rendering loop. It is called once for each * field (i.e., once per frame on * a mono system or once each for the right eye and left eye on a * two-pass stereo system. This is intended for use by applications that * want to mix retained/compiled-retained mode rendering with some * immediate mode rendering. Applications that wish to perform * operations during the rendering loop, may override this * function. * * <p> * Updates to live Geometry, Texture, and ImageComponent objects * in the scene graph are not allowed from this method. * * <p> * NOTE: Applications should <i>not</i> call this method. * <p> * * @param fieldDesc field description, one of: FIELD_LEFT, FIELD_RIGHT or * FIELD_ALL. Applications that wish to work correctly in stereo mode * should render the same image for both FIELD_LEFT and FIELD_RIGHT calls. * If Java 3D calls the renderer with FIELD_ALL then the immediate mode * rendering only needs to be done once. */ public void renderField(int fieldDesc) { // Do nothing; the user overrides this to cause some action } /** * Stop the Java 3D renderer on this Canvas3D object. If the * Java 3D renderer is currently running, the rendering will be * synchronized before being stopped. No further rendering will be done * to this canvas by Java 3D until the renderer is started again. * In pure immediate mode this method should be called prior to adding * this canvas to an active View object. * * @exception IllegalStateException if this Canvas3D is in * off-screen mode. */ public final void stopRenderer() { // Issue 131: renderer can't be stopped only if it is an offscreen, // manual canvas. Otherwise, it has to be seen as an onscreen canvas. if (manualRendering) throw new IllegalStateException(J3dI18N.getString("Canvas3D14")); if (isRunning) { VirtualUniverse.mc.postRequest(MasterControl.STOP_RENDERER, this); isRunning = false; } } /** * Start the Java 3D renderer on this Canvas3D object. If the * Java 3D renderer is not currently running, any rendering to other * Canvas3D objects sharing the same View will be synchronized before this * Canvas3D's renderer is (re)started. When a Canvas3D is created, it is * initially marked as being started. This means that as soon as the * Canvas3D is added to an active View object, the rendering loop will * render the scene graph to the canvas. */ public final void startRenderer() { // Issue 260 : ignore attempt to start renderer if fatal error if (fatalError) { return; } if (!isRunning) { VirtualUniverse.mc.postRequest(MasterControl.START_RENDERER, this); isRunning = true; redraw(); } } /** * Retrieves the state of the renderer for this Canvas3D object. * @return the state of the renderer * * @since Java 3D 1.2 */ public final boolean isRendererRunning() { return isRunning; } // Returns the state of the fatal error flag boolean isFatalError() { return fatalError; } // Sets the fatal error flag to true; stop the renderer for this canvas void setFatalError() { fatalError = true; if (isRunning) { isRunning = false; if (!manualRendering) { VirtualUniverse.mc.postRequest(MasterControl.STOP_RENDERER, this); } } } /** * Retrieves a flag indicating whether this Canvas3D is an * off-screen canvas. * * @return <code>true</code> if this Canvas3D is an off-screen canvas; * <code>false</code> if this is an on-screen canvas. * * @since Java 3D 1.2 */ public boolean isOffScreen() { return offScreen; } /** * Sets the off-screen buffer for this Canvas3D. The specified * image is written into by the Java 3D renderer. The size of the * specified ImageComponent determines the size, in pixels, of * this Canvas3D--the size inherited from Component is ignored. * <p> * NOTE: the size, physical width, and physical height of the associated * Screen3D must be set explicitly prior to rendering. * Failure to do so will result in an exception. * <p> * * @param buffer the image component that will be rendered into by * subsequent calls to renderOffScreenBuffer. The image component must not * be part of a live scene graph, nor may it subsequently be made part of a * live scene graph while being used as an off-screen buffer; an * IllegalSharingException is thrown in such cases. The buffer may be null, * indicating that the previous off-screen buffer is released without a new * buffer being set. * * @exception IllegalStateException if this Canvas3D is not in * off-screen mode. * * @exception RestrictedAccessException if an off-screen rendering * is in process for this Canvas3D. * * @exception IllegalSharingException if the specified ImageComponent2D * is part of a live scene graph * * @exception IllegalSharingException if the specified ImageComponent2D is * being used by an immediate mode context, or by another Canvas3D as * an off-screen buffer. * * @exception IllegalArgumentException if the image class of the specified * ImageComponent2D is <i>not</i> ImageClass.BUFFERED_IMAGE. * * @exception IllegalArgumentException if the specified * ImageComponent2D is in by-reference mode and its * RenderedImage is null. * * @exception IllegalArgumentException if the ImageComponent2D format * is <i>not</i> a 3-component format (e.g., FORMAT_RGB) * or a 4-component format (e.g., FORMAT_RGBA). * * @see #renderOffScreenBuffer * @see Screen3D#setSize(int, int) * @see Screen3D#setSize(Dimension) * @see Screen3D#setPhysicalScreenWidth * @see Screen3D#setPhysicalScreenHeight * * @since Java 3D 1.2 */ public void setOffScreenBuffer(ImageComponent2D buffer) { int width, height; boolean freeCanvasId = false; if (!offScreen) throw new IllegalStateException(J3dI18N.getString("Canvas3D1")); if (offScreenRendering) throw new RestrictedAccessException(J3dI18N.getString("Canvas3D2")); // Check that offScreenBufferPending is not already set J3dDebug.doAssert(!offScreenBufferPending, "!offScreenBufferPending"); if (offScreenBuffer != null && offScreenBuffer != buffer) { ImageComponent2DRetained i2dRetained = (ImageComponent2DRetained) offScreenBuffer.retained; i2dRetained.setUsedByOffScreen(false); } if (buffer != null) { ImageComponent2DRetained bufferRetained = (ImageComponent2DRetained) buffer.retained; if (bufferRetained.byReference && !(bufferRetained.getRefImage(0) instanceof BufferedImage)) { throw new IllegalArgumentException(J3dI18N.getString("Canvas3D15")); } if (bufferRetained.getNumberOfComponents() < 3) { throw new IllegalArgumentException(J3dI18N.getString("Canvas3D16")); } if (buffer.isLive()) { throw new IllegalSharingException(J3dI18N.getString("Canvas3D26")); } if (bufferRetained.getInImmCtx()) { throw new IllegalSharingException(J3dI18N.getString("Canvas3D27")); } if (buffer != offScreenBuffer && bufferRetained.getUsedByOffScreen()) { throw new IllegalSharingException(J3dI18N.getString("Canvas3D28")); } bufferRetained.setUsedByOffScreen(true); width = bufferRetained.width; height = bufferRetained.height; // Issues 347, 348 - assign a canvasId for off-screen Canvas3D if (manualRendering) { sendAllocateCanvasId(); } } else { width = height = 0; // Issues 347, 348 - release canvasId for off-screen Canvas3D if (manualRendering) { freeCanvasId = true; } } if ((offScreenCanvasSize.width != width) || (offScreenCanvasSize.height != height)) { if (drawable != null) { // Fix for Issue 18 and Issue 175 // Will do destroyOffScreenBuffer in the Renderer thread. sendDestroyCtxAndOffScreenBuffer(); drawable = null; } // Issue 396. Since context is invalid here, we should set it to null. ctx = null; // set the canvas dimension according to the buffer dimension offScreenCanvasSize.setSize(width, height); this.setSize(offScreenCanvasSize); if (width > 0 && height > 0) { sendCreateOffScreenBuffer(); } } else if (ctx != null) { removeCtx(); } if (freeCanvasId) { sendFreeCanvasId(); } offScreenBuffer = buffer; synchronized (dirtyMaskLock) { cvDirtyMask[0] |= MOVED_OR_RESIZED_DIRTY; cvDirtyMask[1] |= MOVED_OR_RESIZED_DIRTY; } } /** * Retrieves the off-screen buffer for this Canvas3D. * * @return the current off-screen buffer for this Canvas3D. * * @exception IllegalStateException if this Canvas3D is not in * off-screen mode. * * @since Java 3D 1.2 */ public ImageComponent2D getOffScreenBuffer() { if (!offScreen) throw new IllegalStateException(J3dI18N.getString("Canvas3D1")); return (offScreenBuffer); } /** * Schedules the rendering of a frame into this Canvas3D's * off-screen buffer. The rendering is done from the point of * view of the View object to which this Canvas3D has been added. * No rendering is performed if this Canvas3D object has not been * added to an active View. This method does not wait for the rendering * to actually happen. An application that wishes to know when * the rendering is complete must either subclass Canvas3D and * override the <code>postSwap</code> method, or call * <code>waitForOffScreenRendering</code>. * * @exception NullPointerException if the off-screen buffer is null. * @exception IllegalStateException if this Canvas3D is not in * off-screen mode, or if either the width or the height of * the associated Screen3D's size is <= 0, or if the associated * Screen3D's physical width or height is <= 0. * @exception RestrictedAccessException if an off-screen rendering * is already in process for this Canvas3D or if the Java 3D renderer * is stopped. * * @see #setOffScreenBuffer * @see Screen3D#setSize(int, int) * @see Screen3D#setSize(Dimension) * @see Screen3D#setPhysicalScreenWidth * @see Screen3D#setPhysicalScreenHeight * @see #waitForOffScreenRendering * @see #postSwap * * @since Java 3D 1.2 */ public void renderOffScreenBuffer() { if (!offScreen) throw new IllegalStateException(J3dI18N.getString("Canvas3D1")); // Issue 131: Cannot manually render to an automatic canvas. if (!manualRendering) throw new IllegalStateException(J3dI18N.getString("Canvas3D24")); // Issue 260 : Cannot render if we already have a fatal error if (fatalError) { throw new IllegalRenderingStateException(J3dI18N.getString("Canvas3D30")); } if (offScreenBuffer == null) throw new NullPointerException(J3dI18N.getString("Canvas3D10")); Dimension screenSize = screen.getSize(); if (screenSize.width <= 0) throw new IllegalStateException(J3dI18N.getString("Canvas3D8")); if (screenSize.height <= 0) throw new IllegalStateException(J3dI18N.getString("Canvas3D9")); if (screen.getPhysicalScreenWidth() <= 0.0) throw new IllegalStateException(J3dI18N.getString("Canvas3D12")); if (screen.getPhysicalScreenHeight() <= 0.0) throw new IllegalStateException(J3dI18N.getString("Canvas3D13")); if (offScreenRendering) throw new RestrictedAccessException(J3dI18N.getString("Canvas3D2")); if (!isRunning) throw new RestrictedAccessException(J3dI18N.getString("Canvas3D11")); // Fix to issue 66 if ((!active) || (pendingView == null)) { /* No rendering is performed if this Canvas3D object has not been added to an active View. */ return; } // Issue 131: moved code that determines off-screen boundary to separate // method that is called from the renderer offScreenRendering = true; // Fix to issue 66. /* This is an attempt to do the following check in one atomic operation : ((view != null) && (view.inCanvasCallback)) */ boolean inCanvasCallback = false; try { inCanvasCallback = view.inCanvasCallback; } catch (NullPointerException npe) { /* Do nothing here */ } if (inCanvasCallback) { // Here we assume that view is stable if inCanvasCallback // is true. This assumption is valid among all j3d threads as // all access to view is synchronized by MasterControl. // Issue : user threads access to view isn't synchronize hence // is model will break. if (screen.renderer == null) { // It is possible that screen.renderer = null when this View // is shared by another onScreen Canvas and this callback // is from that Canvas. In this case it need one more // round before the renderer. screen.renderer = Screen3D.deviceRendererMap.get(screen.graphicsDevice); // screen.renderer may equal to null when multiple // screen is used and this Canvas3D is in different // screen sharing the same View not yet initialize. } // if called from render call back, send a message directly to // the renderer message queue, and call renderer doWork // to do the offscreen rendering now if (Thread.currentThread() == screen.renderer) { J3dMessage createMessage = new J3dMessage(); createMessage.threads = J3dThread.RENDER_THREAD; createMessage.type = J3dMessage.RENDER_OFFSCREEN; createMessage.universe = this.view.universe; createMessage.view = this.view; createMessage.args[0] = this; screen.renderer.rendererStructure.addMessage(createMessage); // modify the args to reflect offScreen rendering screen.renderer.args = new Object[4]; screen.renderer.args[0] = new Integer(Renderer.REQUESTRENDER); screen.renderer.args[1] = this; screen.renderer.args[2] = view; // This extra argument 3 is needed in MasterControl to // test whether offscreen Rendering is used or not screen.renderer.args[3] = null; // call renderer doWork directly since we are already in // the renderer thread screen.renderer.doWork(0); } else { // XXXX: // Now we are in trouble, this will cause deadlock if // waitForOffScreenRendering() is invoked J3dMessage createMessage = new J3dMessage(); createMessage.threads = J3dThread.RENDER_THREAD; createMessage.type = J3dMessage.RENDER_OFFSCREEN; createMessage.universe = this.view.universe; createMessage.view = this.view; createMessage.args[0] = this; screen.renderer.rendererStructure.addMessage(createMessage); VirtualUniverse.mc.setWorkForRequestRenderer(); } } else if (Thread.currentThread() instanceof BehaviorScheduler) { // If called from behavior scheduler, send a message directly to // the renderer message queue. // Note that we didn't use // currentThread() == view.universe.behaviorScheduler // since the caller may be another universe Behavior // scheduler. J3dMessage createMessage = new J3dMessage(); createMessage.threads = J3dThread.RENDER_THREAD; createMessage.type = J3dMessage.RENDER_OFFSCREEN; createMessage.universe = this.view.universe; createMessage.view = this.view; createMessage.args[0] = this; screen.renderer.rendererStructure.addMessage(createMessage); VirtualUniverse.mc.setWorkForRequestRenderer(); } else { // send a message to renderBin // Fix for issue 66 : Since view might not been set yet, // we have to use pendingView instead. J3dMessage createMessage = new J3dMessage(); createMessage.threads = J3dThread.UPDATE_RENDER; createMessage.type = J3dMessage.RENDER_OFFSCREEN; createMessage.universe = this.pendingView.universe; createMessage.view = this.pendingView; createMessage.args[0] = this; createMessage.args[1] = offScreenBuffer; VirtualUniverse.mc.processMessage(createMessage); } } /** * Waits for this Canvas3D's off-screen rendering to be done. * This method will wait until the <code>postSwap</code> method of this * off-screen Canvas3D has completed. If this Canvas3D has not * been added to an active view or if the renderer is stopped for this * Canvas3D, then this method will return * immediately. This method must not be called from a render * callback method of an off-screen Canvas3D. * * @exception IllegalStateException if this Canvas3D is not in * off-screen mode, or if this method is called from a render * callback method of an off-screen Canvas3D. * * @see #renderOffScreenBuffer * @see #postSwap * * @since Java 3D 1.2 */ public void waitForOffScreenRendering() { if (!offScreen) { throw new IllegalStateException(J3dI18N.getString("Canvas3D1")); } if (Thread.currentThread() instanceof Renderer) { throw new IllegalStateException(J3dI18N.getString("Canvas3D31")); } while (offScreenRendering) { MasterControl.threadYield(); } } /** * Sets the location of this off-screen Canvas3D. The location is * the upper-left corner of the Canvas3D relative to the * upper-left corner of the corresponding off-screen Screen3D. * The function of this method is similar to that of * <code>Component.setLocation</code> for on-screen Canvas3D * objects. The default location is (0,0). * * @param x the <i>x</i> coordinate of the upper-left corner of * the new location. * @param y the <i>y</i> coordinate of the upper-left corner of * the new location. * * @exception IllegalStateException if this Canvas3D is not in * off-screen mode. * * @since Java 3D 1.2 */ public void setOffScreenLocation(int x, int y) { if (!offScreen) throw new IllegalStateException(J3dI18N.getString("Canvas3D1")); synchronized (cvLock) { offScreenCanvasLoc.setLocation(x, y); } } /** * Sets the location of this off-screen Canvas3D. The location is * the upper-left corner of the Canvas3D relative to the * upper-left corner of the corresponding off-screen Screen3D. * The function of this method is similar to that of * <code>Component.setLocation</code> for on-screen Canvas3D * objects. The default location is (0,0). * * @param p the point defining the upper-left corner of the new * location. * * @exception IllegalStateException if this Canvas3D is not in * off-screen mode. * * @since Java 3D 1.2 */ public void setOffScreenLocation(Point p) { if (!offScreen) throw new IllegalStateException(J3dI18N.getString("Canvas3D1")); synchronized (cvLock) { offScreenCanvasLoc.setLocation(p); } } /** * Retrieves the location of this off-screen Canvas3D. The * location is the upper-left corner of the Canvas3D relative to * the upper-left corner of the corresponding off-screen Screen3D. * The function of this method is similar to that of * <code>Component.getLocation</code> for on-screen Canvas3D * objects. * * @return a new point representing the upper-left corner of the * location of this off-screen Canvas3D. * * @exception IllegalStateException if this Canvas3D is not in * off-screen mode. * * @since Java 3D 1.2 */ public Point getOffScreenLocation() { if (!offScreen) throw new IllegalStateException(J3dI18N.getString("Canvas3D1")); return (new Point(offScreenCanvasLoc)); } /** * Retrieves the location of this off-screen Canvas3D and stores * it in the specified Point object. The location is the * upper-left corner of the Canvas3D relative to the upper-left * corner of the corresponding off-screen Screen3D. The function * of this method is similar to that of * <code>Component.getLocation</code> for on-screen Canvas3D * objects. This version of <code>getOffScreenLocation</code> is * useful if the caller wants to avoid allocating a new Point * object on the heap. * * @param rv Point object into which the upper-left corner of the * location of this off-screen Canvas3D is copied. * If <code>rv</code> is null, a new Point is allocated. * * @return <code>rv</code> * * @exception IllegalStateException if this Canvas3D is not in * off-screen mode. * * @since Java 3D 1.2 */ public Point getOffScreenLocation(Point rv) { if (!offScreen) throw new IllegalStateException(J3dI18N.getString("Canvas3D1")); if (rv == null) return (new Point(offScreenCanvasLoc)); else { rv.setLocation(offScreenCanvasLoc); return rv; } } void endOffScreenRendering() { ImageComponent2DRetained icRetained = (ImageComponent2DRetained) offScreenBuffer.retained; boolean isByRef = icRetained.isByReference(); ImageComponentRetained.ImageData imageData = icRetained.getImageData(false); if (!isByRef) { // If icRetained has a null image ( BufferedImage) if (imageData == null) { assert (!isByRef); icRetained.createBlankImageData(); imageData = icRetained.getImageData(false); } // Check for possible format conversion in imageData else { // Format convert imageData if format is unsupported. icRetained.evaluateExtensions(this); } // read the image from the offscreen buffer readOffScreenBuffer(ctx, icRetained.getImageFormatTypeIntValue(false), icRetained.getImageDataTypeIntValue(), imageData.get(), offScreenCanvasSize.width, offScreenCanvasSize.height); } else { icRetained.geomLock.getLock(); // Create a copy of format converted image in imageData if format is unsupported. icRetained.evaluateExtensions(this); // read the image from the offscreen buffer readOffScreenBuffer(ctx, icRetained.getImageFormatTypeIntValue(false), icRetained.getImageDataTypeIntValue(), imageData.get(), offScreenCanvasSize.width, offScreenCanvasSize.height); // For byRef, we might have to copy buffer back into // the user's referenced ImageComponent2D if (!imageData.isDataByRef()) { if (icRetained.isImageTypeSupported()) { icRetained.copyToRefImage(0); } else { // This method only handle RGBA conversion. icRetained.copyToRefImageWithFormatConversion(0); } } icRetained.geomLock.unLock(); } } /** * Synchronize and swap buffers on a double buffered canvas for * this Canvas3D object. This method should only be called if the * Java 3D renderer has been stopped. In the normal case, the renderer * automatically swaps the buffer. * This method calls the <code>flush(true)</code> methods of the * associated 2D and 3D graphics contexts, if they have been allocated. * * @exception RestrictedAccessException if the Java 3D renderer is * running. * @exception IllegalStateException if this Canvas3D is in * off-screen mode. * * @see #stopRenderer * @see GraphicsContext3D#flush * @see J3DGraphics2D#flush */ public void swap() { if (offScreen) throw new IllegalStateException(J3dI18N.getString("Canvas3D14")); if (isRunning) throw new RestrictedAccessException(J3dI18N.getString("Canvas3D0")); if (!firstPaintCalled) { return; } if (view != null && graphicsContext3D != null) { if ((view.universe != null) && (Thread.currentThread() == view.universe.behaviorScheduler)) { graphicsContext3D.sendRenderMessage(false, GraphicsContext3D.SWAP, null, null); } else { graphicsContext3D.sendRenderMessage(true, GraphicsContext3D.SWAP, null, null); } graphicsContext3D.runMonitor(J3dThread.WAIT); } } void doSwap() { if (firstPaintCalled && useDoubleBuffer) { try { if (validCtx && (ctx != null) && (view != null)) { synchronized (drawingSurfaceObject) { if (validCtx) { if (!drawingSurfaceObject.renderLock()) { graphicsContext3D.runMonitor(J3dThread.NOTIFY); return; } this.syncRender(ctx, true); swapBuffers(ctx, drawable); drawingSurfaceObject.unLock(); } } } } catch (NullPointerException ne) { drawingSurfaceObject.unLock(); } } // Increment the elapsedFrame for the behavior structure // to trigger any interpolators view.universe.behaviorStructure.incElapsedFrames(); graphicsContext3D.runMonitor(J3dThread.NOTIFY); } /** * Wrapper for native createNewContext method. */ Context createNewContext(Context shareCtx, boolean isSharedCtx) { Context retVal = createNewContext(this.drawable, shareCtx, isSharedCtx, this.offScreen); // compute the max available texture units maxAvailableTextureUnits = Math.max(maxTextureUnits, maxTextureImageUnits); // reset 'antialiasingSet' if new context is created for an already existing Canvas3D, // e.g. resizing offscreen Canvas3D antialiasingSet = false; return retVal; } /** * Make the context associated with the specified canvas current. */ final void makeCtxCurrent() { makeCtxCurrent(ctx, drawable); } /** * Make the specified context current. */ final void makeCtxCurrent(Context ctx) { makeCtxCurrent(ctx, drawable); } final void makeCtxCurrent(Context ctx, Drawable drawable) { if (ctx != screen.renderer.currentCtx || drawable != screen.renderer.currentDrawable) { if (!drawingSurfaceObject.isLocked()) { drawingSurfaceObject.renderLock(); useCtx(ctx, drawable); drawingSurfaceObject.unLock(); } else { useCtx(ctx, drawable); } screen.renderer.currentCtx = ctx; screen.renderer.currentDrawable = drawable; } } // Give the pipeline a chance to release the context; the Pipeline may // or may not ignore this call. void releaseCtx() { if (screen.renderer.currentCtx != null) { boolean needLock = !drawingSurfaceObject.isLocked(); if (needLock) { drawingSurfaceObject.renderLock(); } if (releaseCtx(screen.renderer.currentCtx)) { screen.renderer.currentCtx = null; screen.renderer.currentDrawable = null; } if (needLock) { drawingSurfaceObject.unLock(); } } } /** * Sets the position of the manual left eye in image-plate * coordinates. This value determines eye placement when a head * tracker is not in use and the application is directly controlling * the eye position in image-plate coordinates. * In head-tracked mode or when the windowEyePointPolicy is * RELATIVE_TO_FIELD_OF_VIEW or RELATIVE_TO_COEXISTENCE, this value * is ignored. When the * windowEyepointPolicy is RELATIVE_TO_WINDOW only the Z value is * used. * @param position the new manual left eye position */ public void setLeftManualEyeInImagePlate(Point3d position) { this.leftManualEyeInImagePlate.set(position); synchronized (dirtyMaskLock) { cvDirtyMask[0] |= EYE_IN_IMAGE_PLATE_DIRTY; cvDirtyMask[1] |= EYE_IN_IMAGE_PLATE_DIRTY; } redraw(); } /** * Sets the position of the manual right eye in image-plate * coordinates. This value determines eye placement when a head * tracker is not in use and the application is directly controlling * the eye position in image-plate coordinates. * In head-tracked mode or when the windowEyePointPolicy is * RELATIVE_TO_FIELD_OF_VIEW or RELATIVE_TO_COEXISTENCE, this value * is ignored. When the * windowEyepointPolicy is RELATIVE_TO_WINDOW only the Z value is * used. * @param position the new manual right eye position */ public void setRightManualEyeInImagePlate(Point3d position) { this.rightManualEyeInImagePlate.set(position); synchronized (dirtyMaskLock) { cvDirtyMask[0] |= EYE_IN_IMAGE_PLATE_DIRTY; cvDirtyMask[1] |= EYE_IN_IMAGE_PLATE_DIRTY; } redraw(); } /** * Retrieves the position of the user-specified, manual left eye * in image-plate * coordinates and copies that value into the object provided. * @param position the object that will receive the position */ public void getLeftManualEyeInImagePlate(Point3d position) { position.set(this.leftManualEyeInImagePlate); } /** * Retrieves the position of the user-specified, manual right eye * in image-plate * coordinates and copies that value into the object provided. * @param position the object that will receive the position */ public void getRightManualEyeInImagePlate(Point3d position) { position.set(this.rightManualEyeInImagePlate); } /** * Retrieves the actual position of the left eye * in image-plate * coordinates and copies that value into the object provided. * This value is a function of the windowEyepointPolicy, the tracking * enable flag, and the manual left eye position. * @param position the object that will receive the position */ public void getLeftEyeInImagePlate(Point3d position) { if (canvasViewCache != null) { synchronized (canvasViewCache) { position.set(canvasViewCache.getLeftEyeInImagePlate()); } } else { position.set(leftManualEyeInImagePlate); } } /** * Retrieves the actual position of the right eye * in image-plate * coordinates and copies that value into the object provided. * This value is a function of the windowEyepointPolicy, the tracking * enable flag, and the manual right eye position. * @param position the object that will receive the position */ public void getRightEyeInImagePlate(Point3d position) { if (canvasViewCache != null) { synchronized (canvasViewCache) { position.set(canvasViewCache.getRightEyeInImagePlate()); } } else { position.set(rightManualEyeInImagePlate); } } /** * Retrieves the actual position of the center eye * in image-plate * coordinates and copies that value into the object provided. * The center eye is the fictional eye half-way between the left and * right eye. * This value is a function of the windowEyepointPolicy, the tracking * enable flag, and the manual right and left eye positions. * @param position the object that will receive the position * @see #setMonoscopicViewPolicy */ // XXXX: This might not make sense for field-sequential HMD. public void getCenterEyeInImagePlate(Point3d position) { if (canvasViewCache != null) { synchronized (canvasViewCache) { position.set(canvasViewCache.getCenterEyeInImagePlate()); } } else { Point3d cenEye = new Point3d(); cenEye.add(leftManualEyeInImagePlate, rightManualEyeInImagePlate); cenEye.scale(0.5); position.set(cenEye); } } /** * Retrieves the current ImagePlate coordinates to Virtual World * coordinates transform and places it into the specified object. * @param t the Transform3D object that will receive the * transform */ // TODO: Document -- This will return the transform of left plate. public void getImagePlateToVworld(Transform3D t) { if (canvasViewCache != null) { synchronized (canvasViewCache) { t.set(canvasViewCache.getImagePlateToVworld()); } } else { t.setIdentity(); } } /** * Computes the position of the specified AWT pixel value * in image-plate * coordinates and copies that value into the object provided. * @param x the X coordinate of the pixel relative to the upper-left * hand corner of the window. * @param y the Y coordinate of the pixel relative to the upper-left * hand corner of the window. * @param imagePlatePoint the object that will receive the position in * physical image plate coordinates (relative to the lower-left * corner of the screen). */ // TODO: Document -- This transform the pixel location to the left image plate. public void getPixelLocationInImagePlate(int x, int y, Point3d imagePlatePoint) { if (canvasViewCache != null) { synchronized (canvasViewCache) { imagePlatePoint.x = canvasViewCache.getWindowXInImagePlate((double) x); imagePlatePoint.y = canvasViewCache.getWindowYInImagePlate((double) y); imagePlatePoint.z = 0.0; } } else { imagePlatePoint.set(0.0, 0.0, 0.0); } } void getPixelLocationInImagePlate(double x, double y, double z, Point3d imagePlatePoint) { if (canvasViewCache != null) { synchronized (canvasViewCache) { canvasViewCache.getPixelLocationInImagePlate(x, y, z, imagePlatePoint); } } else { imagePlatePoint.set(0.0, 0.0, 0.0); } } /** * Computes the position of the specified AWT pixel value * in image-plate * coordinates and copies that value into the object provided. * @param pixelLocation the coordinates of the pixel relative to * the upper-left hand corner of the window. * @param imagePlatePoint the object that will receive the position in * physical image plate coordinates (relative to the lower-left * corner of the screen). * * @since Java 3D 1.2 */ // TODO: Document -- This transform the pixel location to the left image plate. public void getPixelLocationInImagePlate(Point2d pixelLocation, Point3d imagePlatePoint) { if (canvasViewCache != null) { synchronized (canvasViewCache) { imagePlatePoint.x = canvasViewCache.getWindowXInImagePlate(pixelLocation.x); imagePlatePoint.y = canvasViewCache.getWindowYInImagePlate(pixelLocation.y); imagePlatePoint.z = 0.0; } } else { imagePlatePoint.set(0.0, 0.0, 0.0); } } /** * Projects the specified point from image plate coordinates * into AWT pixel coordinates. The AWT pixel coordinates are * copied into the object provided. * @param imagePlatePoint the position in * physical image plate coordinates (relative to the lower-left * corner of the screen). * @param pixelLocation the object that will receive the coordinates * of the pixel relative to the upper-left hand corner of the window. * * @since Java 3D 1.2 */ // TODO: Document -- This transform the pixel location from the left image plate. public void getPixelLocationFromImagePlate(Point3d imagePlatePoint, Point2d pixelLocation) { if (canvasViewCache != null) { synchronized (canvasViewCache) { canvasViewCache.getPixelLocationFromImagePlate(imagePlatePoint, pixelLocation); } } else { pixelLocation.set(0.0, 0.0); } } /** * Copies the current Vworld projection transform for each eye * into the specified Transform3D objects. This transform takes * points in virtual world coordinates and projects them into * clipping coordinates, which are in the range [-1,1] in * <i>X</i>, <i>Y</i>, and <i>Z</i> after clipping and perspective * division. * In monoscopic mode, the same projection transform will be * copied into both the right and left eye Transform3D objects. * * @param leftProjection the Transform3D object that will receive * a copy of the current projection transform for the left eye. * * @param rightProjection the Transform3D object that will receive * a copy of the current projection transform for the right eye. * * @since Java 3D 1.3 */ public void getVworldProjection(Transform3D leftProjection, Transform3D rightProjection) { if (canvasViewCache != null) { ViewPlatformRetained viewPlatformRetained = (ViewPlatformRetained) view.getViewPlatform().retained; synchronized (canvasViewCache) { leftProjection.mul(canvasViewCache.getLeftProjection(), canvasViewCache.getLeftVpcToEc()); leftProjection.mul(viewPlatformRetained.getVworldToVpc()); // caluclate right eye if in stereo, otherwise // this is the same as the left eye. if (useStereo) { rightProjection.mul(canvasViewCache.getRightProjection(), canvasViewCache.getRightVpcToEc()); rightProjection.mul(viewPlatformRetained.getVworldToVpc()); } else { rightProjection.set(leftProjection); } } } else { leftProjection.setIdentity(); rightProjection.setIdentity(); } } /** * Copies the inverse of the current Vworld projection transform * for each eye into the specified Transform3D objects. This * transform takes points in clipping coordinates, which are in * the range [-1,1] in <i>X</i>, <i>Y</i>, and <i>Z</i> after * clipping and perspective division, and transforms them into * virtual world coordinates. * In monoscopic mode, the same inverse projection transform will * be copied into both the right and left eye Transform3D objects. * * @param leftInverseProjection the Transform3D object that will * receive a copy of the current inverse projection transform for * the left eye. * @param rightInverseProjection the Transform3D object that will * receive a copy of the current inverse projection transform for * the right eye. * * @since Java 3D 1.3 */ public void getInverseVworldProjection(Transform3D leftInverseProjection, Transform3D rightInverseProjection) { if (canvasViewCache != null) { synchronized (canvasViewCache) { leftInverseProjection.set(canvasViewCache.getLeftCcToVworld()); // caluclate right eye if in stereo, otherwise // this is the same as the left eye. if (useStereo) { rightInverseProjection.set(canvasViewCache.getRightCcToVworld()); } else { rightInverseProjection.set(leftInverseProjection); } } } else { leftInverseProjection.setIdentity(); rightInverseProjection.setIdentity(); } } /** * Retrieves the physical width of this canvas window in meters. * @return the physical window width in meters. */ public double getPhysicalWidth() { double width = 0.0; if (canvasViewCache != null) { synchronized (canvasViewCache) { width = canvasViewCache.getPhysicalWindowWidth(); } } return width; } /** * Retrieves the physical height of this canvas window in meters. * @return the physical window height in meters. */ public double getPhysicalHeight() { double height = 0.0; if (canvasViewCache != null) { synchronized (canvasViewCache) { height = canvasViewCache.getPhysicalWindowHeight(); } } return height; } /** * Retrieves the current Virtual World coordinates to ImagePlate * coordinates transform and places it into the specified object. * @param t the Transform3D object that will receive the * transform */ // TODO: Document -- This will return the transform of left plate. public void getVworldToImagePlate(Transform3D t) { if (canvasViewCache != null) { synchronized (canvasViewCache) { t.set(canvasViewCache.getVworldToImagePlate()); } } else { t.setIdentity(); } } void getLastVworldToImagePlate(Transform3D t) { if (canvasViewCache != null) { synchronized (canvasViewCache) { t.set(canvasViewCache.getLastVworldToImagePlate()); } } else { t.setIdentity(); } } /** * Sets view that points to this Canvas3D. * @param view view object that points to this Canvas3D */ void setView(View view) { pendingView = view; // We can't set View directly here in user thread since // other threads may using canvas.view // e.g. In Renderer, we use canvas3d.view.inCallBack // before and after postSwap(), if view change in between // than view.inCallBack may never reset to false. VirtualUniverse.mc.postRequest(MasterControl.SET_VIEW, this); evaluateActive(); } void computeViewCache() { synchronized (cvLock) { if (view == null) { canvasViewCache = null; canvasViewCacheFrustum = null; } else { canvasViewCache = new CanvasViewCache(this, screen.screenViewCache, view.viewCache); // Issue 109 : construct a separate canvasViewCache for // computing view frustum canvasViewCacheFrustum = new CanvasViewCache(this, screen.screenViewCache, view.viewCache); synchronized (dirtyMaskLock) { cvDirtyMask[0] = VIEW_INFO_DIRTY; cvDirtyMask[1] = VIEW_INFO_DIRTY; } } } } /** * Gets view that points to this Canvas3D. * @return view object that points to this Canvas3D */ public View getView() { return pendingView; } /** * Returns a status flag indicating whether or not stereo * is available. * This is equivalent to: * <ul> * <code> * ((Boolean)queryProperties(). * get("stereoAvailable")). * booleanValue() * </code> * </ul> * * @return a flag indicating whether stereo is available */ public boolean getStereoAvailable() { return ((Boolean) queryProperties().get("stereoAvailable")).booleanValue(); } /** * Turns stereo on or off. Note that this attribute is used * only when stereo is available. Enabling stereo on a Canvas3D * that does not support stereo has no effect. * @param flag enables or disables the display of stereo * * @see #queryProperties */ public void setStereoEnable(boolean flag) { stereoEnable = flag; useStereo = stereoEnable && stereoAvailable; synchronized (dirtyMaskLock) { cvDirtyMask[0] |= STEREO_DIRTY; cvDirtyMask[1] |= STEREO_DIRTY; } redraw(); } /** * Returns a status flag indicating whether or not stereo * is enabled. * @return a flag indicating whether stereo is enabled */ public boolean getStereoEnable() { return this.stereoEnable; } /** * Specifies how Java 3D generates monoscopic view. If set to * View.LEFT_EYE_VIEW, the view generated corresponds to the view as * seen from the left eye. If set to View.RIGHT_EYE_VIEW, the view * generated corresponds to the view as seen from the right * eye. If set to View.CYCLOPEAN_EYE_VIEW, the view generated * corresponds to the view as seen from the 'center eye', the * fictional eye half-way between the left and right eye. The * default monoscopic view policy is View.CYCLOPEAN_EYE_VIEW. * <p> * NOTE: for backward compatibility with Java 3D 1.1, if this * attribute is set to its default value of * View.CYCLOPEAN_EYE_VIEW, the monoscopic view policy in the * View object will be used. An application should not use both * the deprecated View method and this Canvas3D method at the same * time. * @param policy one of View.LEFT_EYE_VIEW, View.RIGHT_EYE_VIEW, or * View.CYCLOPEAN_EYE_VIEW. * * @exception IllegalStateException if the specified * policy is CYCLOPEAN_EYE_VIEW, the canvas is a stereo canvas, * and the viewPolicy for the associated view is HMD_VIEW * * @since Java 3D 1.2 */ public void setMonoscopicViewPolicy(int policy) { if ((view != null) && (view.viewPolicy == View.HMD_VIEW) && (monoscopicViewPolicy == View.CYCLOPEAN_EYE_VIEW) && (!useStereo)) { throw new IllegalStateException(J3dI18N.getString("View31")); } monoscopicViewPolicy = policy; synchronized (dirtyMaskLock) { cvDirtyMask[0] |= MONOSCOPIC_VIEW_POLICY_DIRTY; cvDirtyMask[1] |= MONOSCOPIC_VIEW_POLICY_DIRTY; } redraw(); } /** * Returns policy on how Java 3D generates monoscopic view. * @return policy one of View.LEFT_EYE_VIEW, View.RIGHT_EYE_VIEW or * View.CYCLOPEAN_EYE_VIEW. * * @since Java 3D 1.2 */ public int getMonoscopicViewPolicy() { return this.monoscopicViewPolicy; } /** * Returns a status flag indicating whether or not double * buffering is available. * This is equivalent to: * <ul> * <code> * ((Boolean)queryProperties(). * get("doubleBufferAvailable")). * booleanValue() * </code> * </ul> * * @return a flag indicating whether double buffering is available. */ public boolean getDoubleBufferAvailable() { return ((Boolean) queryProperties().get("doubleBufferAvailable")).booleanValue(); } /** * Turns double buffering on or off. If double buffering * is off, all drawing is to the front buffer and no buffer swap * is done between frames. It should be stressed that running * Java 3D with double buffering disabled is not recommended. * Enabling double buffering on a Canvas3D * that does not support double buffering has no effect. * * @param flag enables or disables double buffering. * * @see #queryProperties */ public void setDoubleBufferEnable(boolean flag) { doubleBufferEnable = flag; useDoubleBuffer = doubleBufferEnable && doubleBufferAvailable; if (Thread.currentThread() == screen.renderer) { setRenderMode(ctx, FIELD_ALL, useDoubleBuffer); } redraw(); } /** * Returns a status flag indicating whether or not double * buffering is enabled. * @return a flag indicating if double buffering is enabled. */ public boolean getDoubleBufferEnable() { return doubleBufferEnable; } /** * Returns a status flag indicating whether or not scene * antialiasing is available. * This is equivalent to: * <ul> * <code> * ((Boolean)queryProperties(). * get("sceneAntialiasingAvailable")). * booleanValue() * </code> * </ul> * * @return a flag indicating whether scene antialiasing is available. */ public boolean getSceneAntialiasingAvailable() { return ((Boolean) queryProperties().get("sceneAntialiasingAvailable")).booleanValue(); } /** * Returns a flag indicating whether or not the specified shading * language is supported. A ShaderError will be generated if an * unsupported shading language is used. * * @param shadingLanguage the shading language being queried, one of: * <code>Shader.SHADING_LANGUAGE_GLSL</code> or * <code>Shader.SHADING_LANGUAGE_CG</code>. * * @return true if the specified shading language is supported, * false otherwise. * * @since Java 3D 1.4 */ public boolean isShadingLanguageSupported(int shadingLanguage) { // Call queryProperties to ensure that the shading language flags are valid queryProperties(); if (shadingLanguage == Shader.SHADING_LANGUAGE_GLSL) return shadingLanguageGLSL; return false; } /** * Returns a read-only Map object containing key-value pairs that define * various properties for this Canvas3D. All of the keys are * String objects. The values are key-specific, but most will be * Boolean, Integer, Float, Double, or String objects. * * <p> * The currently defined keys are: * * <p> * <ul> * <table BORDER=1 CELLSPACING=1 CELLPADDING=1> * <tr> * <td><b>Key (String)</b></td> * <td><b>Value Type</b></td> * </tr> * <tr> * <td><code>shadingLanguageCg</code></td> * <td>Boolean</td> * </tr> * <tr> * <td><code>shadingLanguageGLSL</code></td> * <td>Boolean</td> * </tr> * <tr> * <td><code>doubleBufferAvailable</code></td> * <td>Boolean</td> * </tr> * <tr> * <td><code>stereoAvailable</code></td> * <td>Boolean</td> * </tr> * <tr> * <td><code>sceneAntialiasingAvailable</code></td> * <td>Boolean</td> * </tr> * <tr> * <td><code>sceneAntialiasingNumPasses</code></td> * <td>Integer</td> * </tr> * <tr> * <td><code>stencilSize</code></td> * <td>Integer</td> * </tr> * <tr> * <td><code>texture3DAvailable</code></td> * <td>Boolean</td> * </tr> * <tr> * <td><code>textureColorTableSize</code></td> * <td>Integer</td> * </tr> * <tr> * <td><code>textureLodRangeAvailable</code></td> * <td>Boolean</td> * </tr> * <tr> * <td><code>textureLodOffsetAvailable</code></td> * <td>Boolean</td> * </tr> * <tr> * <td><code>textureWidthMax</code></td> * <td>Integer</td> * </tr> * <tr> * <td><code>textureHeightMax</code></td> * <td>Integer</td> * </tr> * <tr> * <td><code>textureBoundaryWidthMax</code></td> * <td>Integer</td> * </tr> * <tr> * <td><code>textureEnvCombineAvailable</code></td> * <td>Boolean</td> * </tr> * <tr> * <td><code>textureCombineDot3Available</code></td> * <td>Boolean</td> * </tr> * <tr> * <td><code>textureCombineSubtractAvailable</code></td> * <td>Boolean</td> * </tr> * <tr> * <td><code>textureCoordSetsMax</code></td> * <td>Integer</td> * </tr> * <tr> * <td><code>textureUnitStateMax</code></td> * <td>Integer</td> * </tr> * <tr> * <td><code>textureImageUnitsMax</code></td> * <td>Integer</td> * </tr> * <tr> * <td><code>textureImageUnitsVertexMax</code></td> * <td>Integer</td> * </tr> * <tr> * <td><code>textureImageUnitsCombinedMax</code></td> * <td>Integer</td> * </tr> * <tr> * <td><code>textureCubeMapAvailable</code></td> * <td>Boolean</td> * </tr> * <tr> * <td><code>textureDetailAvailable</code></td> * <td>Boolean</td> * </tr> * <tr> * <td><code>textureSharpenAvailable</code></td> * <td>Boolean</td> * </tr> * <tr> * <td><code>textureFilter4Available</code></td> * <td>Boolean</td> * </tr> * <tr> * <td><code>textureAnisotropicFilterDegreeMax</code></td> * <td>Float</td> * </tr> * <tr> * <td><code>textureNonPowerOfTwoAvailable</code></td> * <td>Boolean</td> * </tr> * <tr> * <td><code>vertexAttrsMax</code></td> * <td>Integer</td> * </tr> * <tr> * <td><code>compressedGeometry.majorVersionNumber</code></td> * <td>Integer</td> * </tr> * <tr> * <td><code>compressedGeometry.minorVersionNumber</code></td> * <td>Integer</td> * </tr> * <tr> * <td><code>compressedGeometry.minorMinorVersionNumber</code></td> * <td>Integer</td> * </tr> * <tr> * <td><code>native.version</code></td> * <td>String</td> * </tr> * </table> * </ul> * * <p> * The descriptions of the values returned for each key are as follows: * * <p> * <ul> * <li> * <code>shadingLanguageCg</code> * <ul> * A Boolean indicating whether or not Cg shading Language * is available for this Canvas3D. * </ul> * </li> * * <li> * <code>shadingLanguageGLSL</code> * <ul> * A Boolean indicating whether or not GLSL shading Language * is available for this Canvas3D. * </ul> * </li> * * <li> * <code>doubleBufferAvailable</code> * <ul> * A Boolean indicating whether or not double buffering * is available for this Canvas3D. This is equivalent to * the getDoubleBufferAvailable method. If this flag is false, * the Canvas3D will be rendered in single buffer mode; requests * to enable double buffering will be ignored. * </ul> * </li> * * <li> * <code>stereoAvailable</code> * <ul> * A Boolean indicating whether or not stereo * is available for this Canvas3D. This is equivalent to * the getStereoAvailable method. If this flag is false, * the Canvas3D will be rendered in monoscopic mode; requests * to enable stereo will be ignored. * </ul> * </li> * * <li> * <code>sceneAntialiasingAvailable</code> * <ul> * A Boolean indicating whether or not scene antialiasing * is available for this Canvas3D. This is equivalent to * the getSceneAntialiasingAvailable method. If this flag is false, * requests to enable scene antialiasing will be ignored. * </ul> * </li> * * <li> * <code>sceneAntialiasingNumPasses</code> * <ul> * An Integer indicating the number of passes scene antialiasing * requires to render a single frame for this Canvas3D. * If this value is zero, scene antialiasing is not supported. * If this value is one, multisampling antialiasing is used. * Otherwise, the number indicates the number of rendering passes * needed. * </ul> * </li> * * <li> * <code>stencilSize</code> * <ul> * An Integer indicating the number of stencil bits that are available * for this Canvas3D. * </ul> * </li> * * <li> * <code>texture3DAvailable</code> * <ul> * A Boolean indicating whether or not 3D Texture mapping * is available for this Canvas3D. If this flag is false, * 3D texture mapping is either not supported by the underlying * rendering layer or is otherwise unavailable for this * particular Canvas3D. All use of 3D texture mapping will be * ignored in this case. * </ul> * </li> * * <li> * <code>textureColorTableSize</code> * <ul> * An Integer indicating the maximum size of the texture color * table for this Canvas3D. If the size is 0, the texture * color table is either not supported by the underlying rendering * layer or is otherwise unavailable for this particular * Canvas3D. An attempt to use a texture color table larger than * textureColorTableSize will be ignored; no color lookup will be * performed. * </ul> * </li> * * <li> * <code>textureLodRangeAvailable</code> * <ul> * A Boolean indicating whether or not setting only a subset of mipmap * levels and setting a range of texture LOD are available for this * Canvas3D. * If it indicates false, setting a subset of mipmap levels and * setting a texture LOD range are not supported by the underlying * rendering layer, and an attempt to set base level, or maximum level, * or minimum LOD, or maximum LOD will be ignored. In this case, * images for all mipmap levels must be defined for the texture to be * valid. * </ul> * </li> * * <li> * <code>textureLodOffsetAvailable</code> * <ul> * A Boolean indicating whether or not setting texture LOD offset is * available for this Canvas3D. If it indicates false, setting * texture LOD offset is not supported by the underlying rendering * layer, and an attempt to set the texture LOD offset will be ignored. * </ul> * </li> * * <li> * <code>textureWidthMax</code> * <ul> * An Integer indicating the maximum texture width supported by * this Canvas3D. If the width of a texture exceeds the maximum texture * width for a Canvas3D, then the texture will be effectively disabled * for that Canvas3D. * </ul> * </li> * * <li> * <code>textureHeightMax</code> * <ul> * An Integer indicating the maximum texture height supported by * this Canvas3D. If the height of a texture exceeds the maximum texture * height for a Canvas3D, then the texture will be effectively disabled * for that Canvas3D. * </ul> * </li> * * <li> * <code>textureBoundaryWidthMax</code> * <ul> * An Integer indicating the maximum texture boundary width * supported by the underlying rendering layer for this Canvas3D. If * the maximum supported texture boundary width is 0, then texture * boundary is not supported by the underlying rendering layer. * An attempt to specify a texture boundary width > the * textureBoundaryWidthMax will effectively disable the texture. * </ul> * </li> * * <li> * <code>textureEnvCombineAvailable</code> * <ul> * A Boolean indicating whether or not texture environment combine * operation is supported for this Canvas3D. If it indicates false, * then texture environment combine is not supported by the * underlying rendering layer, and an attempt to specify COMBINE * as the texture mode will be ignored. The texture mode in effect * will be REPLACE. * </ul> * </li> * * <li> * <code>textureCombineDot3Available</code> * <ul> * A Boolean indicating whether or not texture combine mode * COMBINE_DOT3 is * supported for this Canvas3D. If it indicates false, then * texture combine mode COMBINE_DOT3 is not supported by * the underlying rendering layer, and an attempt to specify * COMBINE_DOT3 as the texture combine mode will be ignored. * The texture combine mode in effect will be COMBINE_REPLACE. * </ul> * </li> * * <li> * <code>textureCombineSubtractAvailable</code> * <ul> * A Boolean indicating whether or not texture combine mode * COMBINE_SUBTRACT is * supported for this Canvas3D. If it indicates false, then * texture combine mode COMBINE_SUBTRACT is not supported by * the underlying rendering layer, and an attempt to specify * COMBINE_SUBTRACT as the texture combine mode will be ignored. * The texture combine mode in effect will be COMBINE_REPLACE. * </ul> * </li> * * <li> * <code>textureCoordSetsMax</code> * <ul> * An Integer indicating the maximum number of texture coordinate sets * supported by the underlying rendering layer. * </ul> * </li> * * <li> * <code>textureUnitStateMax</code> * <ul> * An Integer indicating the maximum number of fixed-function texture units * supported by the underlying rendering layer. If the number of * application-sepcified texture unit states exceeds the maximum number * for a Canvas3D, and the fixed-function rendering pipeline is used, then * the texture will be effectively disabled for that Canvas3D. * </ul> * </li> * * <li> * <code>textureImageUnitsMax</code> * <ul> * An Integer indicating the maximum number of texture image units * that can be accessed by the fragment shader when programmable shaders * are used. * </ul> * </li> * * <li> * <code>textureImageUnitsVertexMax</code> * <ul> * An Integer indicating the maximum number of texture image units * that can be accessed by the vertex shader when programmable shaders * are used. * </ul> * </li> * * <li> * <code>textureImageUnitsCombinedMax</code> * <ul> * An Integer indicating the combined maximum number of texture image units * that can be accessed by the vertex shader and the fragment shader when * programmable shaders are used. * </ul> * </li> * * <li> * <code>textureCubeMapAvailable</code> * <ul> * A Boolean indicating whether or not texture cube map is supported * for this Canvas3D. If it indicates false, then texture cube map * is not supported by the underlying rendering layer, and an attempt * to specify NORMAL_MAP or REFLECTION_MAP as the texture generation * mode will be ignored. The texture generation mode in effect will * be SPHERE_MAP. * </ul> * </li> * * <li> * <code>textureDetailAvailable</code> * <ul> * A Boolean indicating whether or not detail texture is supported * for this Canvas3D. If it indicates false, then detail texture is * not supported by the underlying rendering layer, and an attempt * to specify LINEAR_DETAIL, LINEAR_DETAIL_ALPHA or * LINEAR_DETAIL_RGB as the texture magnification filter mode will * be ignored. The texture magnification filter mode in effect will * be BASE_LEVEL_LINEAR. * As of Java 3D 1.5, this property is always false. * </ul> * </li> * * <li> * <code>textureSharpenAvailable</code> * <ul> * A Boolean indicating whether or not sharpen texture is supported * for this Canvas3D. If it indicates false, then sharpen texture * is not supported by the underlying rendering layer, and an attempt * to specify LINEAR_SHARPEN, LINEAR_SHARPEN_ALPHA or * LINEAR_SHARPEN_RGB as the texture magnification filter mode * will be ignored. The texture magnification filter mode in effect * will be BASE_LEVEL_LINEAR. * </ul> * </li> * * <li> * <code>textureFilter4Available</code> * <ul> * A Boolean indicating whether or not filter4 is supported for this * Canvas3D. If it indicates flase, then filter4 is not supported * by the underlying rendering layer, and an attempt to specify * FILTER_4 as the texture minification filter mode or texture * magnification filter mode will be ignored. The texture filter mode * in effect will be BASE_LEVEL_LINEAR. * </ul> * </li> * * <li> * <code>textureAnisotropicFilterDegreeMax</code> * <ul> * A Float indicating the maximum degree of anisotropic filter * available for this Canvas3D. If it indicates 1.0, setting * anisotropic filter is not supported by the underlying rendering * layer, and an attempt to set anisotropic filter degree will be ignored. * </ul> * </li> * <li> * <code>textureNonPowerOfTwoAvailable</code> * <ul> * A Boolean indicating whether or not texture dimensions that are * not powers of two are supported for * for this Canvas3D. If it indicates false, then textures with * non power of two sizes will be ignored. Set the property * j3d.textureEnforcePowerOfTwo to revert to the pre-1.5 behavior * of throwing exceptions for non power of two textures. * </ul> * </li> * * <li> * <code>vertexAttrsMax</code> * <ul> * An Integer indicating the maximum number of vertex attributes * supported by the underlying rendering layer. This is in addition to * the vertex coordinate (position), color, normal, and so forth. * </ul> * </li> * * <li> * <code>compressedGeometry.majorVersionNumber</code><br> * <code>compressedGeometry.minorVersionNumber</code><br> * <code>compressedGeometry.minorMinorVersionNumber</code> * <ul> * Integers indicating the major, minor, and minor-minor * version numbers, respectively, of the version of compressed * geometry supported by this version of Java 3D. * </ul> * </li> * * <li> * <code>native.version</code> * <ul> * A String indicating the version number of the native graphics * library. The format of this string is defined by the native * library. * </ul> * </li> * </ul> * * @return the properties of this Canavs3D * * @since Java 3D 1.2 */ public final Map queryProperties() { if (queryProps == null) { boolean createDummyCtx = false; synchronized (VirtualUniverse.mc.contextCreationLock) { if (ctx == null) { createDummyCtx = true; } } if (createDummyCtx) { GraphicsConfigTemplate3D.setQueryProps(this); } //create query Properties createQueryProps(); } if (fatalError) { throw new IllegalStateException(J3dI18N.getString("Canvas3D29")); } return queryProps; } void createQueryContext() { // create a dummy context to query for support of certain // extensions, the context will destroy immediately // inside the native code after setting the various // fields in this object createQueryContext(drawable, offScreen, 1, 1); // compute the max available texture units maxAvailableTextureUnits = Math.max(maxTextureUnits, maxTextureImageUnits); } /** * Creates the query properties for this Canvas. */ private void createQueryProps() { // Create lists of keys and values ArrayList<String> keys = new ArrayList<String>(); ArrayList<Object> values = new ArrayList<Object>(); int pass = 0; // properties not associated with graphics context keys.add("doubleBufferAvailable"); values.add(new Boolean(doubleBufferAvailable)); keys.add("stereoAvailable"); values.add(new Boolean(stereoAvailable)); keys.add("sceneAntialiasingAvailable"); values.add(new Boolean(sceneAntialiasingAvailable)); keys.add("sceneAntialiasingNumPasses"); if (sceneAntialiasingAvailable) { pass = (sceneAntialiasingMultiSamplesAvailable ? 1 : Renderer.NUM_ACCUMULATION_SAMPLES); } values.add(new Integer(pass)); keys.add("stencilSize"); // Return the actual stencil size if the user owns it, otherwise // return 0 if (userStencilAvailable) { values.add(new Integer(actualStencilSize)); } else { values.add(new Integer(0)); } keys.add("compressedGeometry.majorVersionNumber"); values.add(new Integer(GeometryDecompressor.majorVersionNumber)); keys.add("compressedGeometry.minorVersionNumber"); values.add(new Integer(GeometryDecompressor.minorVersionNumber)); keys.add("compressedGeometry.minorMinorVersionNumber"); values.add(new Integer(GeometryDecompressor.minorMinorVersionNumber)); // Properties associated with graphics context keys.add("texture3DAvailable"); values.add(new Boolean((textureExtendedFeatures & TEXTURE_3D) != 0)); keys.add("textureColorTableSize"); values.add(new Integer(textureColorTableSize)); keys.add("textureEnvCombineAvailable"); values.add(new Boolean((textureExtendedFeatures & TEXTURE_COMBINE) != 0)); keys.add("textureCombineDot3Available"); values.add(new Boolean((textureExtendedFeatures & TEXTURE_COMBINE_DOT3) != 0)); keys.add("textureCombineSubtractAvailable"); values.add(new Boolean((textureExtendedFeatures & TEXTURE_COMBINE_SUBTRACT) != 0)); keys.add("textureCubeMapAvailable"); values.add(new Boolean((textureExtendedFeatures & TEXTURE_CUBE_MAP) != 0)); keys.add("textureSharpenAvailable"); values.add(new Boolean((textureExtendedFeatures & TEXTURE_SHARPEN) != 0)); keys.add("textureDetailAvailable"); values.add(new Boolean((textureExtendedFeatures & TEXTURE_DETAIL) != 0)); keys.add("textureFilter4Available"); values.add(new Boolean((textureExtendedFeatures & TEXTURE_FILTER4) != 0)); keys.add("textureAnisotropicFilterDegreeMax"); values.add(new Float(anisotropicDegreeMax)); keys.add("textureWidthMax"); values.add(new Integer(textureWidthMax)); keys.add("textureHeightMax"); values.add(new Integer(textureHeightMax)); keys.add("texture3DWidthMax"); values.add(new Integer(texture3DWidthMax)); keys.add("texture3DHeightMax"); values.add(new Integer(texture3DHeightMax)); keys.add("texture3DDepthMax"); values.add(new Integer(texture3DDepthMax)); keys.add("textureBoundaryWidthMax"); values.add(new Integer(textureBoundaryWidthMax)); keys.add("textureLodRangeAvailable"); values.add(new Boolean((textureExtendedFeatures & TEXTURE_LOD_RANGE) != 0)); keys.add("textureLodOffsetAvailable"); values.add(new Boolean((textureExtendedFeatures & TEXTURE_LOD_OFFSET) != 0)); keys.add("textureNonPowerOfTwoAvailable"); values.add(new Boolean((textureExtendedFeatures & TEXTURE_NON_POWER_OF_TWO) != 0)); keys.add("textureAutoMipMapGenerationAvailable"); values.add(new Boolean((textureExtendedFeatures & TEXTURE_AUTO_MIPMAP_GENERATION) != 0)); keys.add("textureCoordSetsMax"); values.add(new Integer(maxTexCoordSets)); keys.add("textureUnitStateMax"); values.add(new Integer(maxTextureUnits)); keys.add("textureImageUnitsMax"); values.add(new Integer(maxTextureImageUnits)); keys.add("textureImageUnitsVertexMax"); values.add(new Integer(maxVertexTextureImageUnits)); keys.add("textureImageUnitsCombinedMax"); values.add(new Integer(maxCombinedTextureImageUnits)); keys.add("vertexAttrsMax"); values.add(new Integer(maxVertexAttrs)); keys.add("shadingLanguageGLSL"); values.add(new Boolean(shadingLanguageGLSL)); keys.add("native.version"); values.add(nativeGraphicsVersion); keys.add("native.vendor"); values.add(nativeGraphicsVendor); keys.add("native.renderer"); values.add(nativeGraphicsRenderer); // Now Create read-only properties object queryProps = new J3dQueryProps(keys, values); } /** * Update the view cache associated with this canvas. */ void updateViewCache(boolean flag, CanvasViewCache cvc, BoundingBox frustumBBox, boolean doInfinite) { assert cvc == null; synchronized (cvLock) { if (firstPaintCalled && (canvasViewCache != null)) { assert canvasViewCacheFrustum != null; // Issue 109 : choose the appropriate cvCache if (frustumBBox != null) { canvasViewCacheFrustum.snapshot(true); canvasViewCacheFrustum.computeDerivedData(flag, null, frustumBBox, doInfinite); } else { canvasViewCache.snapshot(false); canvasViewCache.computeDerivedData(flag, null, null, doInfinite); } } } } /** * Set depthBufferWriteEnableOverride flag */ void setDepthBufferWriteEnableOverride(boolean flag) { depthBufferWriteEnableOverride = flag; } /** * Set depthBufferEnableOverride flag */ void setDepthBufferEnableOverride(boolean flag) { depthBufferEnableOverride = flag; } // Static initializer for Canvas3D class static { VirtualUniverse.loadLibraries(); } void resetTexture(Context ctx, int texUnitIndex) { // D3D also need to reset texture attributes this.resetTextureNative(ctx, texUnitIndex); if (texUnitIndex < 0) { texUnitIndex = 0; } texUnitState[texUnitIndex].mirror = null; texUnitState[texUnitIndex].texture = null; } // reset all attributes so that everything e.g. display list, // texture will recreate again in the next frame void resetRendering() { reset(); synchronized (dirtyMaskLock) { cvDirtyMask[0] |= VIEW_INFO_DIRTY; cvDirtyMask[1] |= VIEW_INFO_DIRTY; } } void reset() { int i; currentAppear = new AppearanceRetained(); currentMaterial = new MaterialRetained(); viewFrustum = new CachedFrustum(); canvasDirty = 0xffff; lightBin = null; environmentSet = null; attributeBin = null; shaderBin = null; textureBin = null; renderMolecule = null; polygonAttributes = null; lineAttributes = null; pointAttributes = null; material = null; enableLighting = false; transparency = null; coloringAttributes = null; shaderProgram = null; texture = null; texAttrs = null; if (texUnitState != null) { TextureUnitStateRetained tus; for (i = 0; i < texUnitState.length; i++) { tus = texUnitState[i]; if (tus != null) { tus.texAttrs = null; tus.texGen = null; } } } texCoordGeneration = null; renderingAttrs = null; appearance = null; appHandle = null; dirtyRenderMoleculeList.clear(); displayListResourceFreeList.clear(); dirtyDlistPerRinfoList.clear(); textureIdResourceFreeList.clear(); lightChanged = true; modelMatrix = null; modelClip = null; fog = null; sceneAmbient = new Color3f(); for (i = 0; i < frameCount.length; i++) { frameCount[i] = -1; } for (i = 0; i < lights.length; i++) { lights[i] = null; } if (currentLights != null) { for (i = 0; i < currentLights.length; i++) { currentLights[i] = null; } } enableMask = -1; stateUpdateMask = 0; depthBufferWriteEnableOverride = false; depthBufferEnableOverride = false; depthBufferWriteEnable = true; vfPlanesValid = false; lightChanged = false; for (i = 0; i < curStateToUpdate.length; i++) { curStateToUpdate[i] = null; } // Issue 362 - need to reset display lists and ctxTimeStamp in this // method, so that display lists will be recreated when canvas is // removed from a view and then added back into a view with another // canvas needToRebuildDisplayList = true; ctxTimeStamp = VirtualUniverse.mc.getContextTimeStamp(); } void resetImmediateRendering() { canvasDirty = 0xffff; ra = null; setSceneAmbient(ctx, 0.0f, 0.0f, 0.0f); disableFog(ctx); resetRenderingAttributes(ctx, false, false); resetTexture(ctx, -1); resetTexCoordGeneration(ctx); resetTextureAttributes(ctx); texUnitState[0].texAttrs = null; texUnitState[0].texGen = null; resetPolygonAttributes(ctx); resetLineAttributes(ctx); resetPointAttributes(ctx); resetTransparency(ctx, RenderMolecule.SURFACE, PolygonAttributes.POLYGON_FILL, false, false); resetColoringAttributes(ctx, 1.0f, 1.0f, 1.0f, 1.0f, false); updateMaterial(ctx, 1.0f, 1.0f, 1.0f, 1.0f); resetRendering(); makeCtxCurrent(); synchronized (dirtyMaskLock) { cvDirtyMask[0] |= VIEW_INFO_DIRTY; cvDirtyMask[1] |= VIEW_INFO_DIRTY; } needToRebuildDisplayList = true; ctxTimeStamp = VirtualUniverse.mc.getContextTimeStamp(); } @Override public Point getLocationOnScreen() { try { return super.getLocationOnScreen(); } catch (IllegalComponentStateException e) { } return new Point(); } void setProjectionMatrix(Context ctx, Transform3D projTrans) { this.projTrans = projTrans; setProjectionMatrix(ctx, projTrans.mat); } void setModelViewMatrix(Context ctx, double[] viewMatrix, Transform3D mTrans) { setModelViewMatrix(ctx, viewMatrix, mTrans.mat); if (!useStereo) { this.modelMatrix = mTrans; } else { // TODO : This seems wrong to do only for the right eye. // A possible approach is to invalidate the cache at begin of // each eye. if (rightStereoPass) { // Only set cache in right stereo pass, otherwise // if the left stereo pass set the cache value, // setModelViewMatrix() in right stereo pass will not // perform in RenderMolecules. this.modelMatrix = mTrans; } } } void setDepthBufferWriteEnable(boolean mode) { depthBufferWriteEnable = mode; setDepthBufferWriteEnable(ctx, mode); } void setNumActiveTexUnit(int n) { numActiveTexUnit = n; } int getNumActiveTexUnit() { return numActiveTexUnit; } void setLastActiveTexUnit(int n) { lastActiveTexUnit = n; } int getLastActiveTexUnit() { return lastActiveTexUnit; } // Create the texture state array void createTexUnitState() { texUnitState = new TextureUnitStateRetained[maxAvailableTextureUnits]; for (int t = 0; t < maxAvailableTextureUnits; t++) { texUnitState[t] = new TextureUnitStateRetained(); texUnitState[t].texture = null; texUnitState[t].mirror = null; } } /** * Enable separate specular color if it is not overriden by the * property j3d.disableSeparateSpecular. */ void enableSeparateSpecularColor() { boolean enable = !VirtualUniverse.mc.disableSeparateSpecularColor; updateSeparateSpecularColorEnable(ctx, enable); } // Send a createOffScreenBuffer message to Renderer (via // MasterControl) and wait for it to be done private void sendCreateOffScreenBuffer() { // Wait for the buffer to be created unless called from // a Behavior or from a Rendering thread if (!(Thread.currentThread() instanceof BehaviorScheduler) && !(Thread.currentThread() instanceof Renderer)) { offScreenBufferPending = true; } // Send message to Renderer thread to perform createOffScreenBuffer. VirtualUniverse.mc.sendCreateOffScreenBuffer(this); // Wait for off-screen buffer to be created while (offScreenBufferPending) { // Issue 364: create master control thread if needed VirtualUniverse.mc.createMasterControlThread(); MasterControl.threadYield(); } } // Send a destroyOffScreenBuffer message to Renderer (via // MasterControl) and wait for it to be done private void sendDestroyCtxAndOffScreenBuffer() { // Wait for the buffer to be destroyed unless called from // a Behavior or from a Rendering thread Thread currentThread = Thread.currentThread(); if (!(currentThread instanceof BehaviorScheduler) && !(currentThread instanceof Renderer)) { offScreenBufferPending = true; } // Fix for Issue 18 and Issue 175 // Send message to Renderer thread to perform remove Ctx and destroyOffScreenBuffer. VirtualUniverse.mc.sendDestroyCtxAndOffScreenBuffer(this); // Wait for ctx and off-screen buffer to be destroyed while (offScreenBufferPending) { // Issue 364: create master control thread if needed VirtualUniverse.mc.createMasterControlThread(); MasterControl.threadYield(); } } // Send a allocateCanvasId message to Renderer (via MasterControl) without // waiting for it to be done private void sendAllocateCanvasId() { // Send message to Renderer thread to allocate a canvasId VirtualUniverse.mc.sendAllocateCanvasId(this); } // Send a freeCanvasId message to Renderer (via MasterControl) without // waiting for it to be done private void sendFreeCanvasId() { // Send message to Renderer thread to free the canvasId VirtualUniverse.mc.sendFreeCanvasId(this); } private void removeCtx() { if ((screen != null) && (screen.renderer != null) && (ctx != null)) { VirtualUniverse.mc.postRequest(MasterControl.FREE_CONTEXT, new Object[] { this, Long.valueOf(0L), drawable, ctx }); // Fix for Issue 19 // Wait for the context to be freed unless called from // a Behavior or from a Rendering thread Thread currentThread = Thread.currentThread(); if (!(currentThread instanceof BehaviorScheduler) && !(currentThread instanceof Renderer)) { while (ctxTimeStamp != 0) { MasterControl.threadYield(); } } ctx = null; } } /** * Serialization of Canvas3D objects is not supported. * * @exception UnsupportedOperationException this method is not supported * * @since Java 3D 1.3 */ private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { throw new UnsupportedOperationException(J3dI18N.getString("Canvas3D20")); } /** * Serialization of Canvas3D objects is not supported. * * @exception UnsupportedOperationException this method is not supported * * @since Java 3D 1.3 */ private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { throw new UnsupportedOperationException(J3dI18N.getString("Canvas3D20")); } // mark that the current bin specified by the bit is already updated void setStateIsUpdated(int bit) { stateUpdateMask &= ~(1 << bit); } // mark that the bin specified by the bit needs to be updated void setStateToUpdate(int bit, Object bin) { stateUpdateMask |= 1 << bit; curStateToUpdate[bit] = bin; } // update LightBin, EnvironmentSet, AttributeBin & ShaderBin if neccessary // according to the stateUpdateMask static int ENV_STATE_MASK = (1 << LIGHTBIN_BIT) | (1 << ENVIRONMENTSET_BIT) | (1 << ATTRIBUTEBIN_BIT) | (1 << SHADERBIN_BIT); void updateEnvState() { if ((stateUpdateMask & ENV_STATE_MASK) == 0) return; if ((stateUpdateMask & (1 << LIGHTBIN_BIT)) != 0) { ((LightBin) curStateToUpdate[LIGHTBIN_BIT]).updateAttributes(this); } if ((stateUpdateMask & (1 << ENVIRONMENTSET_BIT)) != 0) { ((EnvironmentSet) curStateToUpdate[ENVIRONMENTSET_BIT]).updateAttributes(this); } if ((stateUpdateMask & (1 << ATTRIBUTEBIN_BIT)) != 0) { ((AttributeBin) curStateToUpdate[ATTRIBUTEBIN_BIT]).updateAttributes(this); } if ((stateUpdateMask & (1 << SHADERBIN_BIT)) != 0) { ((ShaderBin) curStateToUpdate[SHADERBIN_BIT]).updateAttributes(this); } // reset the state update mask for those environment state bits stateUpdateMask &= ~ENV_STATE_MASK; } /** * update state if neccessary according to the stateUpdatedMask */ void updateState(int dirtyBits) { if (stateUpdateMask == 0) return; updateEnvState(); if ((stateUpdateMask & (1 << TEXTUREBIN_BIT)) != 0) { ((TextureBin) curStateToUpdate[TEXTUREBIN_BIT]).updateAttributes(this); } if ((stateUpdateMask & (1 << RENDERMOLECULE_BIT)) != 0) { ((RenderMolecule) curStateToUpdate[RENDERMOLECULE_BIT]).updateAttributes(this, dirtyBits); } if ((stateUpdateMask & (1 << TRANSPARENCY_BIT)) != 0) { ((RenderMolecule) curStateToUpdate[RENDERMOLECULE_BIT]).updateTransparencyAttributes(this); stateUpdateMask &= ~(1 << TRANSPARENCY_BIT); } // reset state update mask stateUpdateMask = 0; } // This method updates this Texture2D for raster. // Note : No multi-texture is not used. void updateTextureForRaster(Texture2DRetained texture) { // Setup texture and texture attributes for texture unit 0. Pipeline.getPipeline().updateTextureUnitState(ctx, 0, true); setLastActiveTexUnit(0); setNumActiveTexUnit(1); texture.updateNative(this); resetTexCoordGeneration(ctx); resetTextureAttributes(ctx); for (int i = 1; i < maxTextureUnits; i++) { resetTexture(ctx, i); } // set the active texture unit back to 0 activeTextureUnit(ctx, 0); // Force the next textureBin to reload. canvasDirty |= Canvas3D.TEXTUREBIN_DIRTY | Canvas3D.TEXTUREATTRIBUTES_DIRTY; } void restoreTextureBin() { // Need to check TextureBin's shaderBin for null // TextureBin can get clear() if there isn't any RM under it. if ((textureBin != null) && (textureBin.shaderBin != null)) { textureBin.updateAttributes(this); } } void textureFill(RasterRetained raster, Point2d winCoord, float mapZ, float alpha) { int winWidth = canvasViewCache.getCanvasWidth(); int winHeight = canvasViewCache.getCanvasHeight(); int rasterImageWidth = raster.image.width; int rasterImageHeight = raster.image.height; float texMinU = 0, texMinV = 0, texMaxU = 0, texMaxV = 0; float mapMinX = 0, mapMinY = 0, mapMaxX = 0, mapMaxY = 0; Point rasterSrcOffset = new Point(); raster.getSrcOffset(rasterSrcOffset); Dimension rasterSize = new Dimension(); raster.getSize(rasterSize); // System.err.println("rasterImageWidth " + rasterImageWidth + " rasterImageHeight " + rasterImageHeight); // System.err.println("rasterSrcOffset " + rasterSrcOffset + " rasterSize " + rasterSize); int rasterMinX = rasterSrcOffset.x; int rasterMaxX = rasterSrcOffset.x + rasterSize.width; int rasterMinY = rasterSrcOffset.y; int rasterMaxY = rasterSrcOffset.y + rasterSize.height; if ((rasterMinX >= rasterImageWidth) || (rasterMinY >= rasterImageHeight) || (rasterMaxX <= 0) || (rasterMaxY <= 0)) { return; } if (rasterMinX < 0) { rasterMinX = 0; } if (rasterMinY < 0) { rasterMinY = 0; } if (rasterMaxX > rasterImageWidth) { rasterMaxX = rasterImageWidth; } if (rasterMaxY > rasterImageHeight) { rasterMaxY = rasterImageHeight; } texMinU = (float) rasterMinX / (float) rasterImageWidth; texMaxU = (float) rasterMaxX / (float) rasterImageWidth; mapMinX = (float) winCoord.x / (float) winWidth; mapMaxX = (float) (winCoord.x + (rasterMaxX - rasterMinX)) / (float) winWidth; if (raster.image.isYUp()) { texMinV = (float) rasterMinY / (float) rasterImageHeight; texMaxV = (float) rasterMaxY / (float) rasterImageHeight; } else { // System.err.println("In yUp is false case"); texMinV = 1.0f - (float) rasterMaxY / (float) rasterImageHeight; texMaxV = 1.0f - (float) rasterMinY / (float) rasterImageHeight; } mapMinY = 1.0f - ((float) (winCoord.y + (rasterMaxY - rasterMinY)) / (float) winHeight); mapMaxY = 1.0f - ((float) winCoord.y / (float) winHeight); textureFillRaster(ctx, texMinU, texMaxU, texMinV, texMaxV, mapMinX, mapMaxX, mapMinY, mapMaxY, mapZ, alpha, raster.image.useBilinearFilter()); } void textureFill(BackgroundRetained bg, int winWidth, int winHeight) { final int maxX = bg.image.width; final int maxY = bg.image.height; // System.err.println("maxX " + maxX + " maxY " + maxY); float xzoom = (float) winWidth / maxX; float yzoom = (float) winHeight / maxY; float zoom = 0; float texMinU = 0, texMinV = 0, texMaxU = 0, texMaxV = 0, adjustV = 0; float mapMinX = 0, mapMinY = 0, mapMaxX = 0, mapMaxY = 0; float halfWidth = 0, halfHeight = 0; switch (bg.imageScaleMode) { case Background.SCALE_NONE: texMinU = 0.0f; texMinV = 0.0f; texMaxU = 1.0f; texMaxV = 1.0f; halfWidth = (float) winWidth / 2.0f; halfHeight = (float) winHeight / 2.0f; mapMinX = (float) ((0 - halfWidth) / halfWidth); mapMinY = (float) ((0 - halfHeight) / halfHeight); mapMaxX = (float) ((maxX - halfWidth) / halfWidth); mapMaxY = (float) ((maxY - halfHeight) / halfHeight); adjustV = ((float) winHeight - (float) maxY) / halfHeight; mapMinY += adjustV; mapMaxY += adjustV; break; case Background.SCALE_FIT_MIN: zoom = Math.min(xzoom, yzoom); texMinU = 0.0f; texMinV = 0.0f; texMaxU = 1.0f; texMaxV = 1.0f; mapMinX = -1.0f; mapMaxY = 1.0f; if (xzoom < yzoom) { mapMaxX = 1.0f; mapMinY = -1.0f + 2.0f * (1.0f - zoom * (float) maxY / (float) winHeight); } else { mapMaxX = -1.0f + zoom * (float) maxX / winWidth * 2; mapMinY = -1.0f; } break; case Background.SCALE_FIT_MAX: zoom = Math.max(xzoom, yzoom); mapMinX = -1.0f; mapMinY = -1.0f; mapMaxX = 1.0f; mapMaxY = 1.0f; if (xzoom < yzoom) { texMinU = 0.0f; texMinV = 0.0f; texMaxU = (float) winWidth / maxX / zoom; texMaxV = 1.0f; } else { texMinU = 0.0f; texMinV = 1.0f - (float) winHeight / maxY / zoom; texMaxU = 1.0f; texMaxV = 1.0f; } break; case Background.SCALE_FIT_ALL: texMinU = 0.0f; texMinV = 0.0f; texMaxU = 1.0f; texMaxV = 1.0f; mapMinX = -1.0f; mapMinY = -1.0f; mapMaxX = 1.0f; mapMaxY = 1.0f; break; case Background.SCALE_REPEAT: texMinU = 0.0f; texMinV = -yzoom; texMaxU = xzoom; texMaxV = 0.0f; mapMinX = -1.0f; mapMinY = -1.0f; mapMaxX = 1.0f; mapMaxY = 1.0f; break; case Background.SCALE_NONE_CENTER: // TODO : Why is there a zoom ? if (xzoom >= 1.0f) { texMinU = 0.0f; texMaxU = 1.0f; mapMinX = -(float) maxX / winWidth; mapMaxX = (float) maxX / winWidth; } else { texMinU = 0.5f - (float) winWidth / maxX / 2; texMaxU = 0.5f + (float) winWidth / maxX / 2; mapMinX = -1.0f; mapMaxX = 1.0f; } if (yzoom >= 1.0f) { texMinV = 0.0f; texMaxV = 1.0f; mapMinY = -(float) maxY / winHeight; mapMaxY = (float) maxY / winHeight; } else { texMinV = 0.5f - (float) winHeight / maxY / 2; texMaxV = 0.5f + (float) winHeight / maxY / 2; mapMinY = -1.0f; mapMaxY = 1.0f; } break; } // System.err.println("Java 3D : mapMinX " + mapMinX + " mapMinY " + mapMinY + // " mapMaxX " + mapMaxX + " mapMaxY " + mapMaxY); textureFillBackground(ctx, texMinU, texMaxU, texMinV, texMaxV, mapMinX, mapMaxX, mapMinY, mapMaxY, bg.image.useBilinearFilter()); } void clear(BackgroundRetained bg, int winWidth, int winHeight) { // Issue 239 - clear stencil if requested and available // Note that this is a partial solution, since we eventually want an API // to control this. boolean clearStencil = VirtualUniverse.mc.stencilClear && userStencilAvailable; clear(ctx, bg.color.x, bg.color.y, bg.color.z, clearStencil); // TODO : This is a bug on not mirror bg. Will fix this as a bug after 1.5 beta. // For now, as a workaround, we will check bg.image and bg.image.imageData not null. if ((bg.image != null) && (bg.image.imageData != null)) { // setup Texture pipe. updateTextureForRaster(bg.texture); textureFill(bg, winWidth, winHeight); // Restore texture pipe. restoreTextureBin(); } } /** * obj is either TextureRetained or DetailTextureImage * if obj is DetailTextureImage then we just clear * the resourceCreationMask of all the formats * no matter it is create or not since we don't * remember the format information for simplicity. * We don't need to check duplicate value of id in the * table since this procedure is invoke only when id * of texture is -1 one time only. * This is always call from Renderer thread. */ void addTextureResource(int id, TextureRetained obj) { if (id <= 0) { return; } if (useSharedCtx) { screen.renderer.addTextureResource(id, obj); } else { // This will replace the previous key if exists if (textureIDResourceTable.size() <= id) { for (int i = textureIDResourceTable.size(); i < id; i++) { textureIDResourceTable.add(null); } textureIDResourceTable.add(obj); } else { textureIDResourceTable.set(id, obj); } } } // handle free resource in the FreeList void freeResourcesInFreeList(Context ctx) { Iterator<Integer> it; int val; // free resource for those canvases that // don't use shared ctx if (displayListResourceFreeList.size() > 0) { for (it = displayListResourceFreeList.iterator(); it.hasNext();) { val = it.next().intValue(); if (val <= 0) { continue; } Canvas3D.freeDisplayList(ctx, val); } displayListResourceFreeList.clear(); } if (textureIdResourceFreeList.size() > 0) { for (it = textureIdResourceFreeList.iterator(); it.hasNext();) { val = it.next().intValue(); if (val <= 0) { continue; } if (val >= textureIDResourceTable.size()) { System.err.println("Error in freeResourcesInFreeList : ResourceIDTableSize = " + textureIDResourceTable.size() + " val = " + val); } else { TextureRetained tex = textureIDResourceTable.get(val); if (tex != null) { synchronized (tex.resourceLock) { tex.resourceCreationMask &= ~canvasBit; if (tex.resourceCreationMask == 0) { tex.freeTextureId(val); } } } textureIDResourceTable.set(val, null); } Canvas3D.freeTexture(ctx, val); } textureIdResourceFreeList.clear(); } } void freeContextResources(Renderer rdr, boolean freeBackground, Context ctx) { TextureRetained tex; // Just return if we don't have a valid renderer or context if (rdr == null || ctx == null) { return; } if (freeBackground) { // Dispose of Graphics2D Texture if (graphics2D != null) { graphics2D.dispose(); } } for (int id = textureIDResourceTable.size() - 1; id >= 0; id--) { tex = textureIDResourceTable.get(id); if (tex == null) { continue; } // Issue 403 : this assertion doesn't hold in some cases // TODO KCR : determine why this is the case // assert id == ((TextureRetained)obj).objectId; Canvas3D.freeTexture(ctx, id); synchronized (tex.resourceLock) { tex.resourceCreationMask &= ~canvasBit; if (tex.resourceCreationMask == 0) { tex.freeTextureId(id); } } } textureIDResourceTable.clear(); freeAllDisplayListResources(ctx); } void freeAllDisplayListResources(Context ctx) { if ((view != null) && (view.renderBin != null)) { view.renderBin.freeAllDisplayListResources(this, ctx); if (useSharedCtx) { // We need to rebuild all other Canvas3D resource // shared by this Canvas3D. Since we didn't // remember resource in Renderer but RenderBin only. if ((screen != null) && (screen.renderer != null)) { screen.renderer.needToRebuildDisplayList = true; } } } } // ***************************************************************** // Wrappers for native methods go below here // ***************************************************************** // This is the native method for creating the underlying graphics context. private Context createNewContext(Drawable drawable, Context shareCtx, boolean isSharedCtx, boolean offScreen) { return Pipeline.getPipeline().createNewContext(this, drawable, shareCtx, isSharedCtx, offScreen); } private void createQueryContext(Drawable drawable, boolean offScreen, int width, int height) { Pipeline.getPipeline().createQueryContext(this, drawable, offScreen, width, height); } // This is the native for creating offscreen buffer Drawable createOffScreenBuffer(Context ctx, int width, int height) { return Pipeline.getPipeline().createOffScreenBuffer(this, ctx, width, height); } void destroyOffScreenBuffer(Context ctx, Drawable drawable) { assert drawable != null; Pipeline.getPipeline().destroyOffScreenBuffer(this, ctx, drawable); } // This is the native for reading the image from the offscreen buffer private void readOffScreenBuffer(Context ctx, int format, int type, Object data, int width, int height) { Pipeline.getPipeline().readOffScreenBuffer(this, ctx, format, type, data, width, height); } // The native method for swapBuffers void swapBuffers(Context ctx, Drawable drawable) { Pipeline.getPipeline().swapBuffers(this, ctx, drawable); } // ----------------------------------------------------------------------------- // native method for setting Material when no material is present void updateMaterial(Context ctx, float r, float g, float b, float a) { Pipeline.getPipeline().updateMaterialColor(ctx, r, g, b, a); } static void destroyContext(Drawable drawable, Context ctx) { Pipeline.getPipeline().destroyContext(drawable, ctx); } // This is the native method for doing accumulation. void accum(Context ctx, float value) { Pipeline.getPipeline().accum(ctx, value); } // This is the native method for doing accumulation return. void accumReturn(Context ctx) { Pipeline.getPipeline().accumReturn(ctx); } // This is the native method for clearing the accumulation buffer. void clearAccum(Context ctx) { Pipeline.getPipeline().clearAccum(ctx); } // This is the native method for getting the number of lights the underlying // native library can support. int getNumCtxLights(Context ctx) { return Pipeline.getPipeline().getNumCtxLights(ctx); } // Native method for decal 1st child setup boolean decal1stChildSetup(Context ctx) { return Pipeline.getPipeline().decal1stChildSetup(ctx); } // Native method for decal nth child setup void decalNthChildSetup(Context ctx) { Pipeline.getPipeline().decalNthChildSetup(ctx); } // Native method for decal reset void decalReset(Context ctx, boolean depthBufferEnable) { Pipeline.getPipeline().decalReset(ctx, depthBufferEnable); } // Native method for decal reset void ctxUpdateEyeLightingEnable(Context ctx, boolean localEyeLightingEnable) { Pipeline.getPipeline().ctxUpdateEyeLightingEnable(ctx, localEyeLightingEnable); } // The following three methods are used in multi-pass case // native method for setting blend color void setBlendColor(Context ctx, float red, float green, float blue, float alpha) { Pipeline.getPipeline().setBlendColor(ctx, red, green, blue, alpha); } // native method for setting blend func void setBlendFunc(Context ctx, int src, int dst) { Pipeline.getPipeline().setBlendFunc(ctx, src, dst); } // native method for setting fog enable flag void setFogEnableFlag(Context ctx, boolean enableFlag) { Pipeline.getPipeline().setFogEnableFlag(ctx, enableFlag); } boolean isAntialiasingSet() { return antialiasingSet; } // Setup the full scene antialising in D3D and ogl when GL_ARB_multisamle supported void setFullSceneAntialiasing(Context ctx, boolean enable) { Pipeline.getPipeline().setFullSceneAntialiasing(ctx, enable); antialiasingSet = enable; } // Native method to update separate specular color control void updateSeparateSpecularColorEnable(Context ctx, boolean control) { Pipeline.getPipeline().updateSeparateSpecularColorEnable(ctx, control); } // True under Solaris, // False under windows when display mode <= 8 bit private boolean validGraphicsMode() { return Pipeline.getPipeline().validGraphicsMode(); } // native method for setting light enables void setLightEnables(Context ctx, long enableMask, int maxLights) { Pipeline.getPipeline().setLightEnables(ctx, enableMask, maxLights); } // native method for setting scene ambient void setSceneAmbient(Context ctx, float red, float green, float blue) { Pipeline.getPipeline().setSceneAmbient(ctx, red, green, blue); } // native method for disabling fog void disableFog(Context ctx) { Pipeline.getPipeline().disableFog(ctx); } // native method for disabling modelClip void disableModelClip(Context ctx) { Pipeline.getPipeline().disableModelClip(ctx); } // native method for setting default RenderingAttributes void resetRenderingAttributes(Context ctx, boolean depthBufferWriteEnableOverride, boolean depthBufferEnableOverride) { Pipeline.getPipeline().resetRenderingAttributes(ctx, depthBufferWriteEnableOverride, depthBufferEnableOverride); } // native method for setting default texture void resetTextureNative(Context ctx, int texUnitIndex) { Pipeline.getPipeline().resetTextureNative(ctx, texUnitIndex); } // native method for activating a particular texture unit void activeTextureUnit(Context ctx, int texUnitIndex) { Pipeline.getPipeline().activeTextureUnit(ctx, texUnitIndex); } // native method for setting default TexCoordGeneration void resetTexCoordGeneration(Context ctx) { Pipeline.getPipeline().resetTexCoordGeneration(ctx); } // native method for setting default TextureAttributes void resetTextureAttributes(Context ctx) { Pipeline.getPipeline().resetTextureAttributes(ctx); } // native method for setting default PolygonAttributes void resetPolygonAttributes(Context ctx) { Pipeline.getPipeline().resetPolygonAttributes(ctx); } // native method for setting default LineAttributes void resetLineAttributes(Context ctx) { Pipeline.getPipeline().resetLineAttributes(ctx); } // native method for setting default PointAttributes void resetPointAttributes(Context ctx) { Pipeline.getPipeline().resetPointAttributes(ctx); } // native method for setting default TransparencyAttributes void resetTransparency(Context ctx, int geometryType, int polygonMode, boolean lineAA, boolean pointAA) { Pipeline.getPipeline().resetTransparency(ctx, geometryType, polygonMode, lineAA, pointAA); } // native method for setting default ColoringAttributes void resetColoringAttributes(Context ctx, float r, float g, float b, float a, boolean enableLight) { Pipeline.getPipeline().resetColoringAttributes(ctx, r, g, b, a, enableLight); } /** * This native method makes sure that the rendering for this canvas * gets done now. */ void syncRender(Context ctx, boolean wait) { Pipeline.getPipeline().syncRender(ctx, wait); } // The native method that sets this ctx to be the current one static boolean useCtx(Context ctx, Drawable drawable) { return Pipeline.getPipeline().useCtx(ctx, drawable); } // Give the Pipeline a chance to release the context. The return // value indicates whether the context was released. private boolean releaseCtx(Context ctx) { return Pipeline.getPipeline().releaseCtx(ctx); } void clear(Context ctx, float r, float g, float b, boolean clearStencil) { Pipeline.getPipeline().clear(ctx, r, g, b, clearStencil); } void textureFillBackground(Context ctx, float texMinU, float texMaxU, float texMinV, float texMaxV, float mapMinX, float mapMaxX, float mapMinY, float mapMaxY, boolean useBiliearFilter) { Pipeline.getPipeline().textureFillBackground(ctx, texMinU, texMaxU, texMinV, texMaxV, mapMinX, mapMaxX, mapMinY, mapMaxY, useBiliearFilter); } void textureFillRaster(Context ctx, float texMinU, float texMaxU, float texMinV, float texMaxV, float mapMinX, float mapMaxX, float mapMinY, float mapMaxY, float mapZ, float alpha, boolean useBiliearFilter) { Pipeline.getPipeline().textureFillRaster(ctx, texMinU, texMaxU, texMinV, texMaxV, mapMinX, mapMaxX, mapMinY, mapMaxY, mapZ, alpha, useBiliearFilter); } void executeRasterDepth(Context ctx, float posX, float posY, float posZ, int srcOffsetX, int srcOffsetY, int rasterWidth, int rasterHeight, int depthWidth, int depthHeight, int depthType, Object depthData) { Pipeline.getPipeline().executeRasterDepth(ctx, posX, posY, posZ, srcOffsetX, srcOffsetY, rasterWidth, rasterHeight, depthWidth, depthHeight, depthType, depthData); } // The native method for setting the ModelView matrix. void setModelViewMatrix(Context ctx, double[] viewMatrix, double[] modelMatrix) { Pipeline.getPipeline().setModelViewMatrix(ctx, viewMatrix, modelMatrix); } // The native method for setting the Projection matrix. void setProjectionMatrix(Context ctx, double[] projMatrix) { Pipeline.getPipeline().setProjectionMatrix(ctx, projMatrix); } // The native method for setting the Viewport. void setViewport(Context ctx, int x, int y, int width, int height) { Pipeline.getPipeline().resizeOffscreenLayer(this, width, height); Pipeline.getPipeline().setViewport(ctx, x, y, width, height); } // used for display Lists void newDisplayList(Context ctx, int displayListId) { Pipeline.getPipeline().newDisplayList(ctx, displayListId); } void endDisplayList(Context ctx) { Pipeline.getPipeline().endDisplayList(ctx); } void callDisplayList(Context ctx, int id, boolean isNonUniformScale) { Pipeline.getPipeline().callDisplayList(ctx, id, isNonUniformScale); } static void freeDisplayList(Context ctx, int id) { Pipeline.getPipeline().freeDisplayList(ctx, id); } static void freeTexture(Context ctx, int id) { Pipeline.getPipeline().freeTexture(ctx, id); } static int generateTexID(Context ctx) { return Pipeline.getPipeline().generateTexID(ctx); } void texturemapping(Context ctx, int px, int py, int xmin, int ymin, int xmax, int ymax, int texWidth, int texHeight, int rasWidth, int format, int objectId, byte[] image, int winWidth, int winHeight) { Pipeline.getPipeline().texturemapping(ctx, px, py, xmin, ymin, xmax, ymax, texWidth, texHeight, rasWidth, format, objectId, image, winWidth, winHeight); } boolean initTexturemapping(Context ctx, int texWidth, int texHeight, int objectId) { return Pipeline.getPipeline().initTexturemapping(ctx, texWidth, texHeight, objectId); } // Set internal render mode to one of FIELD_ALL, FIELD_LEFT or // FIELD_RIGHT. Note that it is up to the caller to ensure that // stereo is available before setting the mode to FIELD_LEFT or // FIELD_RIGHT. The boolean isTRUE for double buffered mode, FALSE // foe single buffering. void setRenderMode(Context ctx, int mode, boolean doubleBuffer) { Pipeline.getPipeline().setRenderMode(ctx, mode, doubleBuffer); } // Set glDepthMask. void setDepthBufferWriteEnable(Context ctx, boolean mode) { Pipeline.getPipeline().setDepthBufferWriteEnable(ctx, mode); } // Methods to get actual capabilities from Canvas3D boolean hasDoubleBuffer() { return Pipeline.getPipeline().hasDoubleBuffer(this); } boolean hasStereo() { return Pipeline.getPipeline().hasStereo(this); } int getStencilSize() { return Pipeline.getPipeline().getStencilSize(this); } boolean hasSceneAntialiasingMultisample() { return Pipeline.getPipeline().hasSceneAntialiasingMultisample(this); } boolean hasSceneAntialiasingAccum() { return Pipeline.getPipeline().hasSceneAntialiasingAccum(this); } }