/*
* 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 org.scijava.java3d;
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.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.IllegalComponentStateException;
import java.awt.Point;
import java.awt.Window;
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 org.scijava.vecmath.Color3f;
import org.scijava.vecmath.Point2d;
import org.scijava.vecmath.Point3d;
import org.scijava.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.
*
* 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.
*
* 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.
*
* On-screen Rendering vs. Off-screen Rendering
*
* 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.
*
* Off-screen Canvas3Ds must not be added to any Container. Java 3D
* renders to off-screen canvases in response to the
* renderOffScreenBuffer
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.
*
* 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.
*
* The getOffScreenBuffer method retrieves the off-screen
* buffer for this Canvas3D.
*
* 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.
*
* 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).
*
* Accessing and Modifying an Eye's Image Plate Position
*
* 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.
*
* 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.
*
* 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.
*
* Monoscopic View Policy
*
* 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.
*
* Immediate Mode Rendering
*
* 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.
*
* 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.
*
* 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 stopRenderer
method, prior to adding the
* Canvas3D object to an active View object (that is, one that is
* attached to a live ViewPlatform object).
*
* Other Canvas3D methods related to immediate mode rendering are:
*
*
* getGraphicsContext3D
retrieves the immediate-mode
* 3D graphics context associated with this Canvas3D. It creates a
* new graphics context if one does not already exist.
*
* getGraphics2D
retrieves the
* 2D graphics object associated with this Canvas3D. It creates a
* new 2D graphics object if one does not already exist.
*
* swap
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.
*
*
*
* Mixed Mode Rendering
*
* 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.
*
*
* Canvas3D methods related to mixed mode rendering are:
*
*
*
* preRender
called by the Java 3D rendering loop after
* clearing the canvas and before any rendering has been done for
* this frame.
*
* postRender
called by the Java 3D rendering loop after
* completing all rendering to the canvas for this frame and before
* the buffer swap.
*
* postSwap
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.
*
* renderField
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.
*
*
* The above callback methods are called by the Java 3D rendering system
* and should not be called by an application directly.
*
*
* The basic Java 3D stereo rendering loop,
* executed for each Canvas3D, is as follows:
*
* 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
*
*
* The basic Java 3D monoscopic rendering loop is as follows:
*
* 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
*
*
* 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.
*
*
* Serialization
*
* Canvas3D does not support serialization. An attempt to
* serialize a Canvas3D object will result in an
* UnsupportedOperationException being thrown.
*
*
* Additional Information
*
* For more information, see the
* Introduction to the Java 3D API and
* View Model
* 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 graphicsConfigTable =
new Hashtable();
// The native graphics version, vendor, and renderer information
String nativeGraphicsVersion = "";
String nativeGraphicsVendor = "";
String nativeGraphicsRenderer = "";
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 dirtyRenderMoleculeList = new ArrayList();
ArrayList dirtyRenderAtomList = new ArrayList();
// List of (Rm, rInfo) pair of individual dlists that need to be rebuilt
ArrayList dirtyDlistPerRinfoList = new ArrayList();
ArrayList displayListResourceFreeList = new ArrayList();
ArrayList textureIdResourceFreeList = new ArrayList();
// 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();
// Remember OGL context resources to free
// before context is destroy.
// It is used when sharedCtx = false;
ArrayList textureIDResourceTable = new ArrayList(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 containerParentList = new LinkedList();
// 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:
*
* left manual eye in image plate : (0.142, 0.135, 0.4572)
* right manual eye in image plate : (0.208, 0.135, 0.4572)
* stereo enable : true
* double buffer enable : true
* monoscopic view policy : View.CYCLOPEAN_EYE_VIEW
* off-screen mode : false
* off-screen buffer : null
* off-screen location : (0,0)
*
*
* @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
* Updates to live Geometry, Texture, and ImageComponent objects
* in the scene graph are not allowed from this method.
*
*
* NOTE: Applications should not 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.
*
*
* Updates to live Geometry, Texture, and ImageComponent objects
* in the scene graph are not allowed from this method.
*
*
* NOTE: Applications should not 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.
*
*
* Updates to live Geometry, Texture, and ImageComponent objects
* in the scene graph are not allowed from this method.
*
*
* NOTE: Applications should not 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.
*
*
* Updates to live Geometry, Texture, and ImageComponent objects
* in the scene graph are not allowed from this method.
*
*
* NOTE: Applications should not call this method.
*
*
* @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 true
if this Canvas3D is an off-screen canvas;
* false
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.
*
* 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.
*
*
* @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 not 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 not 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 postSwap
method, or call
* waitForOffScreenRendering
.
*
* @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 postSwap
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
* Component.setLocation
for on-screen Canvas3D
* objects. The default location is (0,0).
*
* @param x the x coordinate of the upper-left corner of
* the new location.
* @param y the y 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
* Component.setLocation
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
* Component.getLocation
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
* Component.getLocation
for on-screen Canvas3D
* objects. This version of getOffScreenLocation
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 rv
is null, a new Point is allocated.
*
* @return rv
*
* @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 flush(true)
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
* X , Y , and Z 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 X , Y , and Z 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:
*
*
* ((Boolean)queryProperties().
* get("stereoAvailable")).
* booleanValue()
*
*
*
* @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.
*
* 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:
*
*
* ((Boolean)queryProperties().
* get("doubleBufferAvailable")).
* booleanValue()
*
*
*
* @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:
*
*
* ((Boolean)queryProperties().
* get("sceneAntialiasingAvailable")).
* booleanValue()
*
*
*
* @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:
* Shader.SHADING_LANGUAGE_GLSL
or
* Shader.SHADING_LANGUAGE_CG
.
*
* @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.
*
*
* The currently defined keys are:
*
*
*
*
*
* Key (String)
* Value Type
*
*
* shadingLanguageCg
* Boolean
*
*
* shadingLanguageGLSL
* Boolean
*
*
* doubleBufferAvailable
* Boolean
*
*
* stereoAvailable
* Boolean
*
*
* sceneAntialiasingAvailable
* Boolean
*
*
* sceneAntialiasingNumPasses
* Integer
*
*
* stencilSize
* Integer
*
*
* texture3DAvailable
* Boolean
*
*
* textureColorTableSize
* Integer
*
*
* textureLodRangeAvailable
* Boolean
*
*
* textureLodOffsetAvailable
* Boolean
*
*
* textureWidthMax
* Integer
*
*
* textureHeightMax
* Integer
*
*
* textureBoundaryWidthMax
* Integer
*
*
* textureEnvCombineAvailable
* Boolean
*
*
* textureCombineDot3Available
* Boolean
*
*
* textureCombineSubtractAvailable
* Boolean
*
*
* textureCoordSetsMax
* Integer
*
*
* textureUnitStateMax
* Integer
*
*
* textureImageUnitsMax
* Integer
*
*
* textureImageUnitsVertexMax
* Integer
*
*
* textureImageUnitsCombinedMax
* Integer
*
*
* textureCubeMapAvailable
* Boolean
*
*
* textureDetailAvailable
* Boolean
*
*
* textureSharpenAvailable
* Boolean
*
*
* textureFilter4Available
* Boolean
*
*
* textureAnisotropicFilterDegreeMax
* Float
*
*
* textureNonPowerOfTwoAvailable
* Boolean
*
*
* vertexAttrsMax
* Integer
*
*
* compressedGeometry.majorVersionNumber
* Integer
*
*
* compressedGeometry.minorVersionNumber
* Integer
*
*
* compressedGeometry.minorMinorVersionNumber
* Integer
*
*
* native.version
* String
*
*
*
*
*
* The descriptions of the values returned for each key are as follows:
*
*
*
*
* shadingLanguageCg
*
* A Boolean indicating whether or not Cg shading Language
* is available for this Canvas3D.
*
*
*
*
* shadingLanguageGLSL
*
* A Boolean indicating whether or not GLSL shading Language
* is available for this Canvas3D.
*
*
*
*
* doubleBufferAvailable
*
* 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.
*
*
*
*
* stereoAvailable
*
* 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.
*
*
*
*
* sceneAntialiasingAvailable
*
* 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.
*
*
*
*
* sceneAntialiasingNumPasses
*
* 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.
*
*
*
*
* stencilSize
*
* An Integer indicating the number of stencil bits that are available
* for this Canvas3D.
*
*
*
*
* texture3DAvailable
*
* 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.
*
*
*
*
* textureColorTableSize
*
* 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.
*
*
*
*
* textureLodRangeAvailable
*
* 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.
*
*
*
*
* textureLodOffsetAvailable
*
* 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.
*
*
*
*
* textureWidthMax
*
* 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.
*
*
*
*
* textureHeightMax
*
* 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.
*
*
*
*
* textureBoundaryWidthMax
*
* 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.
*
*
*
*
* textureEnvCombineAvailable
*
* 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.
*
*
*
*
* textureCombineDot3Available
*
* 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.
*
*
*
*
* textureCombineSubtractAvailable
*
* 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.
*
*
*
*
* textureCoordSetsMax
*
* An Integer indicating the maximum number of texture coordinate sets
* supported by the underlying rendering layer.
*
*
*
*
* textureUnitStateMax
*
* 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.
*
*
*
*
* textureImageUnitsMax
*
* An Integer indicating the maximum number of texture image units
* that can be accessed by the fragment shader when programmable shaders
* are used.
*
*
*
*
* textureImageUnitsVertexMax
*
* An Integer indicating the maximum number of texture image units
* that can be accessed by the vertex shader when programmable shaders
* are used.
*
*
*
*
* textureImageUnitsCombinedMax
*
* 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.
*
*
*
*
* textureCubeMapAvailable
*
* 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.
*
*
*
*
* textureDetailAvailable
*
* 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.
*
*
*
*
* textureSharpenAvailable
*
* 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.
*
*
*
*
* textureFilter4Available
*
* 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.
*
*
*
*
* textureAnisotropicFilterDegreeMax
*
* 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.
*
*
*
* textureNonPowerOfTwoAvailable
*
* 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.
*
*
*
*
* vertexAttrsMax
*
* 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.
*
*
*
*
* compressedGeometry.majorVersionNumber
* compressedGeometry.minorVersionNumber
* compressedGeometry.minorMinorVersionNumber
*
* Integers indicating the major, minor, and minor-minor
* version numbers, respectively, of the version of compressed
* geometry supported by this version of Java 3D.
*
*
*
*
* native.version
*
* A String indicating the version number of the native graphics
* library. The format of this string is defined by the native
* library.
*
*
*
*
* @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 keys = new ArrayList();
ArrayList values = new ArrayList();
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 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);
}
}