Java tutorial
/* * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. * */ package javax.media.j3d; import java.awt.Dimension; import java.awt.Point; import java.util.Enumeration; import java.util.Vector; import javax.vecmath.Color3f; import javax.vecmath.Vector3d; /** * A GraphicsContext3D object is used for immediate mode rendering into * a 3D canvas. It is created by, and associated with, a specific * Canvas3D object. A GraphicsContext3D defines methods to set 3D graphics * state and draw 3D geometric primitives. There are no public * constructors of GraphicsContext3D. An application obtains a 3D graphics * context object from the Canvas3D object that the application wishes * to render into by using the getGraphicsContext3D method. A new graphics * context is created if one does not already exist. A new GraphicsContext3D * initializes its state variables to the following defaults: * <UL> * <LI> Background object: null </LI> * <LI> Fog object: null </LI> * <LI> ModelClip object: null </LI> * <LI> Appearance object: null </LI> * <LI> List of Light objects: empty </LI> * <LI> high-res coordinate: (0, 0, 0) </LI> * <LI> modelTransform: identity </LI> * <LI> AuralAttributes object: null </LI> * <LI> List of Sound objects: empty </LI> * <LI> buffer override: false </LI> * <LI> front buffer rendering: false </LI> * <LI> stereo mode: <code>STEREO_BOTH</code> </LI> * </UL> * * <p> * Note that the drawing methods in this class are not necessarily * executed immediately. They may be buffered up for future * execution. Applications must call the * <code><a href="#flush(boolean)">flush</a>(boolean)</code> * method to ensure that the rendering actually happens. The flush * method is implicitly called in the following cases: * * <ul> * <li>The <code>readRaster</code> method calls * <code>flush(true)</code></li> * <li>The <code>Canvas3D.swap</code> method calls * <code>flush(true)</code></li> * <li>The Java 3D renderer calls <code>flush(true)</code> prior to * swapping the buffer for a double buffered on-screen Canvas3D</li> * <li>The Java 3D renderer calls <code>flush(true)</code> prior to * copying into the off-screen buffer of an off-screen Canvas3D</li> * <li>The Java 3D renderer calls <code>flush(false)</code> after * calling the preRender, renderField, postRender, and postSwap * Canvas3D callback methods.</li> * </ul> * * <p> * A single-buffered, pure-immediate mode application must explicitly * call flush to ensure that the graphics will be rendered to the * Canvas3D. * * @see Canvas3D#getGraphicsContext3D */ public class GraphicsContext3D extends Object { /** * Specifies that rendering is done to the left eye. * @see #setStereoMode * @since Java 3D 1.2 */ public static final int STEREO_LEFT = 0; /** * Specifies that rendering is done to the right eye. * @see #setStereoMode * @since Java 3D 1.2 */ public static final int STEREO_RIGHT = 1; /** * Specifies that rendering is done to both eyes. This is the * default. * @see #setStereoMode * @since Java 3D 1.2 */ public static final int STEREO_BOTH = 2; /** * Canvas3D in which this GraphicsContext3D will render. */ Canvas3D canvas3d = null; // // Graphics state // // current user specified graphics state private Background uBackground = null; private Fog uFog = null; private Appearance uAppearance = null; private Vector<Light> uLights = new Vector<Light>(); private HiResCoord uHiRes = new HiResCoord(); private Vector<Sound> uSounds = new Vector<Sound>(); private AuralAttributes uAuralAttributes = null; private boolean uBufferOverride = false; private boolean uFrontBufferRendering = false; private int uStereoMode = STEREO_BOTH; private ModelClip uModelClip = null; // Current rendering graphics state // Current background Background background = null; // Background to use if background is null; BackgroundRetained black = new BackgroundRetained(); // Current fog Fog fog = null; // Current modelClip ModelClip modelClip = null; // Current appearance object Appearance appearance = null; // default appearance retained object AppearanceRetained defaultAppearanceRetained = new AppearanceRetained(); // The vector of lights Vector<Light> lights = new Vector<Light>(); // Current High resolution coordinate HiResCoord hiRes = new HiResCoord(); // Current modeling transform Transform3D modelTransform = new Transform3D(); Transform3D identityTransform = new Transform3D(); Transform3D modelClipTransform = null; Transform3D normalTransform = null; boolean normalTransformNeedToUpdate = true; // The vector of sounds Vector<Sound> sounds = new Vector<Sound>(); // Current AuralAttributes state parameters AuralAttributes auralAttributes = null; // The render object associated with this context LightSet ls = null; // The current list of lights LightRetained[] lightlist = null; // Ambient lights Color3f sceneAmbient = new Color3f(0.0f, 0.0f, 0.0f); // The current number of lights, may be less than lightlist.length int numLights = 0; // Current composite transform: hi-res + modelTransform Transform3D compTransform = new Transform3D(); // Draw transform: hi-res + modelTransform + view Transform3D drawTransform = new Transform3D(); // The view transform (VPC to EC). // NOTE that this is *read-only* Transform3D vpcToEc; // A boolean that indicates the lights have changed boolean lightsChanged = false; // A boolean that indicates the sounds have changed // XXXX: the soundsChanged flag are set like lights methods set // lightsChanged? but where is this supposed to be check??? // lightsChanged tested in 'draw'; but Sound are not processed // in draw. boolean soundsChanged = false; // Buffer override flag; enables frontBufferRendering and stereoMode // attributes. boolean bufferOverride = false; // Forces rendering to the front buffer (if bufferOverride is true) boolean frontBufferRendering = false; // Stereo mode for this buffer (if bufferOverride is true) int stereoMode = STEREO_BOTH; // Read Buffer for reading raster of color image byte[] byteBuffer = new byte[1]; // Read Buffer for reading floating depth image float[] floatBuffer = new float[1]; // Read Buffer for reading integer depth image int[] intBuffer = new int[1]; /** * The cached ColoringAttributes color value. It is * 1.0, 1.0, 1.0 if there is no ColoringAttributes. */ float red = 1.0f; float green = 1.0f; float blue = 1.0f; /** * Cached diffuse color value */ float dRed = 1.0f; float dGreen = 1.0f; float dBlue = 1.0f; /** * The cached TransparencyAttributes transparency value. It is * 0.0 if there is no TransparencyAttributes. */ float alpha = 0.0f; /** * The cached visible flag for geometry. */ boolean visible = true; /** * Cached values for polygonMode, line antialiasing, and point antialiasing */ int polygonMode = PolygonAttributes.POLYGON_FILL; boolean lineAA = false; boolean pointAA = false; /** /** * A boolean indicating whether or not lighting should be on. */ boolean enableLighting = false; private Appearance defaultAppearance = null; private boolean ignoreVertexColors = false; static final int CLEAR = 0; static final int DRAW = 1; static final int SWAP = 2; static final int READ_RASTER = 3; static final int SET_APPEARANCE = 4; static final int SET_BACKGROUND = 5; static final int SET_FOG = 6; static final int SET_LIGHT = 7; static final int INSERT_LIGHT = 8; static final int REMOVE_LIGHT = 9; static final int ADD_LIGHT = 10; static final int SET_HI_RES = 11; static final int SET_MODEL_TRANSFORM = 12; static final int MULTIPLY_MODEL_TRANSFORM = 13; static final int SET_SOUND = 14; static final int INSERT_SOUND = 15; static final int REMOVE_SOUND = 16; static final int ADD_SOUND = 17; static final int SET_AURAL_ATTRIBUTES = 18; static final int SET_BUFFER_OVERRIDE = 19; static final int SET_FRONT_BUFFER_RENDERING = 20; static final int SET_STEREO_MODE = 21; static final int FLUSH = 22; static final int FLUSH2D = 23; static final int DRAWANDFLUSH2D = 24; static final int SET_MODELCLIP = 25; static final int DISPOSE2D = 26; static final int NCOMMANDS = 27; // needs to be incremented // when a new command is to be // added to the list private static Integer[] commands = new Integer[NCOMMANDS]; private static Integer[] stereoModes = { new Integer(STEREO_LEFT), new Integer(STEREO_RIGHT), new Integer(STEREO_BOTH) }; // dirty bits private static final int BUFFER_MODE = 0x1; private int dirtyMask = 0; // multi-texture private int numActiveTexUnit = 0; private int lastActiveTexUnitIndex = 0; // for read raster private volatile boolean readRasterReady = false; // for runMonitor private boolean gcReady = false; private int waiting = 0; /** * Constructs and creates a GraphicsContext3D object with default * values. Users do not call this directly, rather they get a * graphics context from a Canvas3D. */ GraphicsContext3D(Canvas3D canvas3d) { this.canvas3d = canvas3d; } /** * Gets the Canvas3D that created this GraphicsContext3D. * @return the Canvas3D that created this GraphicsContext3D */ public Canvas3D getCanvas3D() { return this.canvas3d; } // // Methods to set/get graphics state // /** * Sets the current Appearance object to the specified * Appearance component object. * The graphics context stores a reference to the specified * Appearance object. This means that the application may modify * individual appearance attributes by using the appropriate * methods on the Appearance object. * If the Appearance object is null, default values will be used * for all appearance attributes - it is as if an * Appearance node were created using the default constructor. * * @param appearance the new Appearance object * * @exception IllegalSharingException if the specified appearance refers * to an ImageComponent2D that is being used by a Canvas3D as * an off-screen buffer. */ public void setAppearance(Appearance appearance) { if (appearance == null) { if (defaultAppearance == null) { defaultAppearance = new Appearance(); } appearance = defaultAppearance; } else { // Check whether any ImageComponent2D referred to by // the new appearance is being used as an off-screen buffer and throw // IllegalSharingException if it is. TextureRetained texRetained; ImageComponent[] images; AppearanceRetained appRetained = (AppearanceRetained) appearance.retained; if (appRetained.texture != null) { assert (appRetained.texUnitState == null); texRetained = appRetained.texture; images = texRetained.getImages(); if (images != null) { for (int i = 0; i < images.length; i++) { if (images[i] != null) { ImageComponentRetained imageRetained = (ImageComponentRetained) images[i].retained; // Do illegal sharing check if (imageRetained.getUsedByOffScreen()) { throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D30")); } } } } } else if (appRetained.texUnitState != null) { for (int j = 0; j < appRetained.texUnitState.length; j++) { texRetained = appRetained.texUnitState[j].texture; images = texRetained.getImages(); if (images != null) { for (int i = 0; i < images.length; i++) { if (images[i] != null) { ImageComponentRetained imageRetained = (ImageComponentRetained) images[i].retained; // Do illegal sharing check if (imageRetained.getUsedByOffScreen()) { throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D30")); } } } } } } } uAppearance = appearance; if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active) || (Thread.currentThread() == canvas3d.screen.renderer)) { doSetAppearance(appearance); } else if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.SET_APPEARANCE, appearance, null); } else { sendRenderMessage(true, GraphicsContext3D.SET_APPEARANCE, appearance, null); } } void doSetAppearance(Appearance appearance) { // Appearance can't be null. See setAppearance(). assert (appearance != null); NodeComponentRetained nc; nc = ((AppearanceRetained) appearance.retained).material; if (nc != null) { nc.setInImmCtx(true); enableLighting = ((MaterialRetained) nc).lightingEnable; dRed = ((MaterialRetained) nc).diffuseColor.x; dGreen = ((MaterialRetained) nc).diffuseColor.y; dBlue = ((MaterialRetained) nc).diffuseColor.z; } else { enableLighting = false; } if (appearance instanceof ShaderAppearance) { // Handle ShaderProgramRetained. ShaderProgramRetained spR = ((ShaderAppearanceRetained) appearance.retained).shaderProgram; if (spR != null) { spR.setInImmCtx(true); Shader[] sArray = spR.getShaders(); if (sArray != null) { for (int i = 0; i < sArray.length; i++) { if (sArray[i] != null) { ((ShaderRetained) sArray[i].retained).setInImmCtx(true); } } } } //Handle ShaderAttributeSetRetained. ShaderAttributeSetRetained sasR = ((ShaderAppearanceRetained) appearance.retained).shaderAttributeSet; if (sasR != null) { sasR.setInImmCtx(true); ShaderAttribute[] saArray = sasR.getAll(); if (saArray != null) { for (int i = 0; i < saArray.length; i++) { if (saArray[i] != null) { ((ShaderAttributeRetained) saArray[i].retained).setInImmCtx(true); } } } } } if (((AppearanceRetained) appearance.retained).texUnitState != null) { TextureUnitStateRetained[] texUnitState = ((AppearanceRetained) appearance.retained).texUnitState; for (int i = 0; i < texUnitState.length; i++) { if (texUnitState[i] != null) { texUnitState[i].setInImmCtx(true); } } } nc = ((AppearanceRetained) appearance.retained).texture; if (nc != null) { nc.setInImmCtx(true); } nc = ((AppearanceRetained) appearance.retained).texCoordGeneration; if (nc != null) { nc.setInImmCtx(true); } nc = ((AppearanceRetained) appearance.retained).textureAttributes; if (nc != null) { nc.setInImmCtx(true); } nc = ((AppearanceRetained) appearance.retained).coloringAttributes; if (nc != null) { nc.setInImmCtx(true); red = ((ColoringAttributesRetained) nc).color.x; green = ((ColoringAttributesRetained) nc).color.y; blue = ((ColoringAttributesRetained) nc).color.z; } else { red = 1.0f; green = 1.0f; blue = 1.0f; } nc = ((AppearanceRetained) appearance.retained).transparencyAttributes; if (nc != null) { nc.setInImmCtx(true); alpha = 1.0f - ((TransparencyAttributesRetained) nc).transparency; } else { alpha = 1.0f; } nc = ((AppearanceRetained) appearance.retained).renderingAttributes; if (nc != null) { nc.setInImmCtx(true); visible = ((RenderingAttributesRetained) nc).visible; } else visible = true; nc = ((AppearanceRetained) appearance.retained).polygonAttributes; if (nc != null) { nc.setInImmCtx(true); polygonMode = ((PolygonAttributesRetained) nc).polygonMode; } else { polygonMode = PolygonAttributes.POLYGON_FILL; } nc = ((AppearanceRetained) appearance.retained).lineAttributes; if (nc != null) { nc.setInImmCtx(true); lineAA = ((LineAttributesRetained) nc).lineAntialiasing; } else { lineAA = false; } nc = ((AppearanceRetained) appearance.retained).pointAttributes; if (nc != null) { if (nc.source.isLive()) nc.setInImmCtx(true); pointAA = ((PointAttributesRetained) nc).pointAntialiasing; } else { pointAA = false; } // Reset the inImmCtx flag of this.appearance. if (this.appearance != null) { AppearanceRetained app = (AppearanceRetained) this.appearance.retained; app.setInImmCtx(false); if (app.material != null) { app.material.setInImmCtx(false); } if (app instanceof ShaderAppearanceRetained) { // Handle ShaderProgramRetained. ShaderProgramRetained spR = ((ShaderAppearanceRetained) app).shaderProgram; if (spR != null) { spR.setInImmCtx(false); Shader[] sArray = spR.getShaders(); if (sArray != null) { for (int i = 0; i < sArray.length; i++) { if (sArray[i] != null) { ((ShaderRetained) sArray[i].retained).setInImmCtx(false); } } } } //Handle ShaderAttributeSetRetained. ShaderAttributeSetRetained sasR = ((ShaderAppearanceRetained) app).shaderAttributeSet; if (sasR != null) { sasR.setInImmCtx(false); ShaderAttribute[] saArray = sasR.getAll(); if (saArray != null) { for (int i = 0; i < saArray.length; i++) { if (saArray[i] != null) { ((ShaderAttributeRetained) saArray[i].retained).setInImmCtx(false); } } } } } if (app.texUnitState != null) { for (int i = 0; i < app.texUnitState.length; i++) { if (app.texUnitState[0] != null) app.texUnitState[0].setInImmCtx(false); } } if (app.texture != null) { app.texture.setInImmCtx(false); } if (app.texCoordGeneration != null) { app.texCoordGeneration.setInImmCtx(false); } if (app.textureAttributes != null) { app.textureAttributes.setInImmCtx(false); } if (app.coloringAttributes != null) { app.coloringAttributes.setInImmCtx(false); } if (app.transparencyAttributes != null) { app.transparencyAttributes.setInImmCtx(false); } if (app.renderingAttributes != null) { app.renderingAttributes.setInImmCtx(false); } if (app.polygonAttributes != null) { app.polygonAttributes.setInImmCtx(false); } if (app.lineAttributes != null) { app.lineAttributes.setInImmCtx(false); } if (app.pointAttributes != null) { app.pointAttributes.setInImmCtx(false); } } ((AppearanceRetained) appearance.retained).setInImmCtx(true); this.appearance = appearance; } /** * Retrieves the current Appearance component object. * @return the current Appearance object */ public Appearance getAppearance() { return this.uAppearance; } /** * Sets the current Background to the specified Background * leaf node object. * The graphics context stores a reference to the specified * Background node. This means that the application may modify * the background color or image by using the appropriate * methods on the Background node. The Background node must * not be part of a live scene graph, nor may it subsequently * be made part of a live scene graph-an IllegalSharingException * is thrown in such cases. If the Background object is null, * the default background color of black (0,0,0) is used to clear * the canvas prior to rendering a new frame. The Background * node's application region is ignored for immediate-mode * rendering. * * @param background the new Background object * * @exception IllegalSharingException if the Background node * is part of or is subsequently made part of a live scene graph. * * @exception IllegalSharingException if the specified background node * refers to an ImageComponent2D that is being used by a Canvas3D as * an off-screen buffer. */ public void setBackground(Background background) { if (background.isLive()) { throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D11")); } BackgroundRetained bgRetained = (BackgroundRetained) background.retained; ImageComponent2D image = bgRetained.getImage(); if (image != null) { ImageComponent2DRetained imageRetained = (ImageComponent2DRetained) image.retained; if (imageRetained.getUsedByOffScreen()) { throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D31")); } } if (((BackgroundRetained) background.retained).geometryBranch != null) { throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D22")); } uBackground = background; if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active) || (Thread.currentThread() == canvas3d.screen.renderer)) { doSetBackground(background); } else if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.SET_BACKGROUND, background, null); } else { sendRenderMessage(true, GraphicsContext3D.SET_BACKGROUND, background, null); } } void doSetBackground(Background background) { BackgroundRetained bg; if (this.background != null) { bg = (BackgroundRetained) this.background.retained; bg.setInImmCtx(false); } bg = (BackgroundRetained) background.retained; bg.setInImmCtx(true); this.background = background; } /** * Retrieves the current Background leaf node object. * @return the current Background object */ public Background getBackground() { return this.uBackground; } /** * Sets the current Fog to the specified Fog * leaf node object. * The graphics context stores a reference to the specified * Fog node. This means that the application may modify the * fog attributes using the appropriate methods on the Fog * node object. The Fog node must not be part of a live * scene graph, nor may it subsequently be made part of a * live scene graph-an IllegalSharingException is thrown in * such cases. If the Fog object is null, fog is disabled. * Both the region of influence and the hierarchical scope * of the Fog node are ignored for immediate-mode rendering. * @param fog the new Fog object * @exception IllegalSharingException if the Fog node * is part of or is subsequently made part of a live scene graph. */ public void setFog(Fog fog) { if (fog != null && fog.isLive()) { throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D12")); } uFog = fog; if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active) || (Thread.currentThread() == canvas3d.screen.renderer)) { doSetFog(fog); } else if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.SET_FOG, fog, null); } else { sendRenderMessage(true, GraphicsContext3D.SET_FOG, fog, null); } } void doSetFog(Fog fog) { if (this.fog != null) { ((FogRetained) this.fog.retained).setInImmCtx(false); } this.fog = fog; if (fog != null) { ((FogRetained) fog.retained).setInImmCtx(true); // Issue 144: updateFogState now called unconditionally updateFogState((FogRetained) fog.retained); } } /** * Retrieves the current Fog leaf node object. * @return the current Fog object */ public Fog getFog() { return this.uFog; } /** * Sets the current ModelClip leaf node to the specified object. * The graphics context stores a reference to the specified * ModelClip node. This means that the application may modify the * model clipping attributes using the appropriate methods on the * ModelClip node object. The ModelClip node must not be part of a * live scene graph, nor may it subsequently be made part of a * live scene graph-an IllegalSharingException is thrown in such * cases. If the ModelClip object is null, model clipping is * disabled. Both the region of influence and the hierarchical * scope of the ModelClip node are ignored for immediate-mode * rendering. * * @param modelClip the new ModelClip node * * @exception IllegalSharingException if the ModelClip node * is part of or is subsequently made part of a live scene graph. * * @since Java 3D 1.2 */ public void setModelClip(ModelClip modelClip) { if ((modelClip != null) && modelClip.isLive()) { throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D25")); } uModelClip = modelClip; if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active) || (Thread.currentThread() == canvas3d.screen.renderer)) { doSetModelClip(modelClip); } else if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.SET_MODELCLIP, modelClip, null); } else { sendRenderMessage(true, GraphicsContext3D.SET_MODELCLIP, modelClip, null); } } void doSetModelClip(ModelClip modelClip) { ModelClipRetained mc = null; this.modelClip = modelClip; if (this.modelClip != null) { mc = (ModelClipRetained) this.modelClip.retained; mc.setInImmCtx(true); if (modelClipTransform == null) modelClipTransform = new Transform3D(); // save the current model Transform modelClipTransform.set(compTransform); } } /** * Retrieves the current ModelClip leaf node object. * @return the current ModelClip object * * @since Java 3D 1.2 */ public ModelClip getModelClip() { return this.uModelClip; } /** * Replaces the specified light with the light provided. * The graphics context stores a reference to each light * object in the list of lights. This means that the * application may modify the light attributes for * any of the lights using the appropriate methods on that * Light node object. None of the Light nodes in the list * of lights may be part of a live scene graph, nor may * they subsequently be made part of a live scene graph - * an IllegalSharingException is thrown in such cases. * @param light the new light * @param index which light to replace * @exception IllegalSharingException if the Light node * is part of or is subsequently made part of a live scene graph. * @exception NullPointerException if the Light object is null. */ public void setLight(Light light, int index) { if (light == null) { throw new NullPointerException(J3dI18N.getString("GraphicsContext3D13")); } if (light.isLive()) { throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D14")); } uLights.set(index, light); if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active) || (Thread.currentThread() == canvas3d.screen.renderer)) { doSetLight(light, index); } else if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.SET_LIGHT, light, new Integer(index)); } else { sendRenderMessage(true, GraphicsContext3D.SET_LIGHT, light, new Integer(index)); } } void doSetLight(Light light, int index) { Light oldlight = this.lights.get(index); if (oldlight != null) { ((LightRetained) oldlight.retained).setInImmCtx(false); } ((LightRetained) light.retained).setInImmCtx(true); updateLightState((LightRetained) light.retained); this.lights.set(index, light); this.lightsChanged = true; } /** * Inserts the specified light at the specified index location. * @param light the new light * @param index at which location to insert * @exception IllegalSharingException if the Light node * is part of or is subsequently made part of a live scene graph. * @exception NullPointerException if the Light object is null. */ public void insertLight(Light light, int index) { if (light == null) { throw new NullPointerException(J3dI18N.getString("GraphicsContext3D13")); } if (light.isLive()) { throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D14")); } uLights.add(index, light); if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active) || (Thread.currentThread() == canvas3d.screen.renderer)) { doInsertLight(light, index); } else if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.INSERT_LIGHT, light, new Integer(index)); } else { sendRenderMessage(true, GraphicsContext3D.INSERT_LIGHT, light, new Integer(index)); } } void doInsertLight(Light light, int index) { ((LightRetained) light.retained).setInImmCtx(true); updateLightState((LightRetained) light.retained); this.lights.add(index, light); this.lightsChanged = true; } /** * Removes the light at the specified index location. * @param index which light to remove */ public void removeLight(int index) { uLights.remove(index); if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active) || (Thread.currentThread() == canvas3d.screen.renderer)) { doRemoveLight(index); } else if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.REMOVE_LIGHT, new Integer(index), null); } else { sendRenderMessage(true, GraphicsContext3D.REMOVE_LIGHT, new Integer(index), null); } } void doRemoveLight(int index) { Light light = this.lights.get(index); ((LightRetained) light.retained).setInImmCtx(false); this.lights.remove(index); this.lightsChanged = true; } /** * Retrieves the index selected light. * @param index which light to return * @return the light at location index */ public Light getLight(int index) { return uLights.get(index); } /** * Retrieves the enumeration object of all the lights. * @return the enumeration object of all the lights */ public Enumeration<Light> getAllLights() { return uLights.elements(); } /** * Appends the specified light to this graphics context's list of lights. * Adding a null Light object to the list will result * in a NullPointerException. Both the region of influence * and the hierarchical scope of all lights in the list * are ignored for immediate-mode rendering. * @param light the light to add * @exception IllegalSharingException if the Light node * is part of or is subsequently made part of a live scene graph. * @exception NullPointerException if the Light object is null. */ public void addLight(Light light) { if (light == null) { throw new NullPointerException(J3dI18N.getString("GraphicsContext3D13")); } if (light.isLive()) { throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D14")); } uLights.addElement(light); if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active) || (Thread.currentThread() == canvas3d.screen.renderer)) { doAddLight(light); } else if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.ADD_LIGHT, light, null); } else { sendRenderMessage(true, GraphicsContext3D.ADD_LIGHT, light, null); } } void doAddLight(Light light) { ((LightRetained) light.retained).setInImmCtx(true); updateLightState((LightRetained) light.retained); this.lights.add(light); this.lightsChanged = true; } /** * Retrieves the current number of lights in this graphics context. * @return the current number of lights */ public int numLights() { return this.uLights.size(); } private Transform3D getNormalTransform() { if (compTransform.isRigid()) { return compTransform; } if (normalTransform == null) { normalTransform = new Transform3D(); } if (normalTransformNeedToUpdate) { normalTransform.invert(compTransform); normalTransform.transpose(); normalTransformNeedToUpdate = false; } return normalTransform; } void updateFogState(FogRetained fogRet) { // Issue 144: update localToVWorldScale for all types of Fog fogRet.setLocalToVworldScale(modelTransform.getDistanceScale()); } void updateLightState(LightRetained light) { if (light instanceof DirectionalLightRetained) { DirectionalLightRetained dl = (DirectionalLightRetained) light; Transform3D xform = getNormalTransform(); xform.transform(dl.direction, dl.xformDirection); dl.xformDirection.normalize(); } else if (light instanceof SpotLightRetained) { SpotLightRetained sl = (SpotLightRetained) light; Transform3D xform = getNormalTransform(); xform.transform(sl.direction, sl.xformDirection); sl.xformDirection.normalize(); this.modelTransform.transform(sl.position, sl.xformPosition); } else if (light instanceof PointLightRetained) { PointLightRetained pl = (PointLightRetained) light; this.modelTransform.transform(pl.position, pl.xformPosition); pl.localToVworldScale = modelTransform.getDistanceScale(); } } /** * Sets the HiRes coordinate of this context to the location * specified by the parameters provided. * The parameters x, y, and z are arrays of eight 32-bit * integers that specify the high-resolution coordinates point. * @param x an eight element array specifying the x position * @param y an eight element array specifying the y position * @param z an eight element array specifying the z position * @see HiResCoord */ public void setHiRes(int[] x, int[] y, int[] z) { HiResCoord hiRes = new HiResCoord(x, y, z); setHiRes(hiRes); } /** * Sets the HiRes coordinate of this context * to the location specified by the HiRes argument. * @param hiRes the HiRes coordinate specifying the a new location */ public void setHiRes(HiResCoord hiRes) { uHiRes.setHiResCoord(hiRes); if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active) || (Thread.currentThread() == canvas3d.screen.renderer)) { doSetHiRes(hiRes); } else if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.SET_HI_RES, hiRes, null); } else { sendRenderMessage(true, GraphicsContext3D.SET_HI_RES, hiRes, null); } } void doSetHiRes(HiResCoord hiRes) { this.hiRes.setHiResCoord(hiRes); computeCompositeTransform(); } /** * Retrieves the current HiRes coordinate of this context. * @param hiRes a HiResCoord object that will receive the * HiRes coordinate of this context */ public void getHiRes(HiResCoord hiRes) { uHiRes.getHiResCoord(hiRes); } /** * Sets the current model transform to a copy of the specified * transform. * A BadTransformException is thrown if an attempt is made * to specify an illegal Transform3D. * @param t the new model transform * @exception BadTransformException if the transform is not affine. */ public void setModelTransform(Transform3D t) { if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active) || (Thread.currentThread() == canvas3d.screen.renderer)) { doSetModelTransform(t); } else { Transform3D uModelTransform = new Transform3D(t); //Transform3D uModelTransform = t; if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.SET_MODEL_TRANSFORM, uModelTransform, null); } else { sendRenderMessage(true, GraphicsContext3D.SET_MODEL_TRANSFORM, uModelTransform, null); } } } void doSetModelTransform(Transform3D t) { this.modelTransform.set(t); computeCompositeTransform(); normalTransformNeedToUpdate = true; } /** * Multiplies the current model transform by the specified * transform and stores the result back into the current * transform. The specified transformation must be affine. * @param t the model transform to be concatenated with the * current model transform * @exception BadTransformException if the transform is not affine. */ public void multiplyModelTransform(Transform3D t) { if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active) || (Thread.currentThread() == canvas3d.screen.renderer)) { doMultiplyModelTransform(t); } else { Transform3D tt = new Transform3D(t); if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.MULTIPLY_MODEL_TRANSFORM, tt, null); } else { sendRenderMessage(true, GraphicsContext3D.MULTIPLY_MODEL_TRANSFORM, tt, null); } } } void doMultiplyModelTransform(Transform3D t) { this.modelTransform.mul(t); computeCompositeTransform(); normalTransformNeedToUpdate = true; } /** * Retrieves the current model transform. * @param t the model transform that will receive the current * model transform */ public void getModelTransform(Transform3D t) { t.set(modelTransform); } /** * Replaces the specified sound with the sound provided. * The graphics context stores a reference to each sound * object in the list of sounds. This means that the * application may modify the sound attributes for * any of the sounds by using the appropriate methods on * that Sound node object. * @param sound the new sound * @param index which sound to replace * @exception IllegalSharingException if the Sound node * is part of or is subsequently made part of a live scene graph. * @exception NullPointerException if the Sound object is null. */ public void setSound(Sound sound, int index) { if (sound == null) { throw new NullPointerException(J3dI18N.getString("GraphicsContext3D17")); } if (sound.isLive()) { throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D23")); } uSounds.set(index, sound); if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active) || (Thread.currentThread() == canvas3d.screen.renderer)) { doSetSound(sound, index); } else if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.SET_SOUND, sound, new Integer(index)); } else { sendRenderMessage(true, GraphicsContext3D.SET_SOUND, sound, new Integer(index)); } } void doSetSound(Sound sound, int index) { Sound oldSound = this.sounds.get(index); ((SoundRetained) sound.retained).setInImmCtx(true); if (oldSound != null) { ((SoundRetained) oldSound.retained).setInImmCtx(false); } ((SoundRetained) sound.retained).setInImmCtx(true); updateSoundState((SoundRetained) (sound.retained)); this.sounds.set(index, sound); this.soundsChanged = true; sendSoundMessage(GraphicsContext3D.SET_SOUND, sound, oldSound); } /** * Inserts the specified sound at the specified index location. * Inserting a sound to the list of sounds implicitly starts the * sound playing. Once a sound is finished playing, it can be * restarted by setting the sound's enable flag to true. * The scheduling region of all sounds in the list is ignored * for immediate-mode rendering. * @param sound the new sound * @param index at which location to insert * @exception IllegalSharingException if the Sound node * is part or is subsequently made part of a live scene graph. * @exception NullPointerException if the Sound object is null. */ public void insertSound(Sound sound, int index) { if (sound == null) { throw new NullPointerException(J3dI18N.getString("GraphicsContext3D17")); } if (sound.isLive()) { throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D23")); } uSounds.add(index, sound); if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active) || (Thread.currentThread() == canvas3d.screen.renderer)) { doInsertSound(sound, index); } else if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.INSERT_SOUND, sound, new Integer(index)); } else { sendRenderMessage(true, GraphicsContext3D.INSERT_SOUND, sound, new Integer(index)); } } void doInsertSound(Sound sound, int index) { updateSoundState((SoundRetained) sound.retained); this.sounds.add(index, sound); this.soundsChanged = true; sendSoundMessage(GraphicsContext3D.INSERT_SOUND, sound, null); } /** * Removes the sound at the specified index location. * @param index which sound to remove */ public void removeSound(int index) { uSounds.remove(index); if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active) || (Thread.currentThread() == canvas3d.screen.renderer)) { doRemoveSound(index); } else if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.REMOVE_SOUND, new Integer(index), null); } else { sendRenderMessage(true, GraphicsContext3D.REMOVE_SOUND, new Integer(index), null); } } void doRemoveSound(int index) { Sound sound = this.sounds.get(index); SoundScheduler soundScheduler = getSoundScheduler(); ((SoundRetained) (sound.retained)).setInImmCtx(false); this.sounds.remove(index); this.soundsChanged = true; // stop sound if playing on audioDevice sendSoundMessage(GraphicsContext3D.REMOVE_SOUND, null, sound); } /** * Retrieves the index selected sound. * @param index which sound to return * @return the sound at location index */ public Sound getSound(int index) { return uSounds.get(index); } /** * Retrieves the enumeration object of all the sounds. * @return the enumeration object of all the sounds */ public Enumeration<Sound> getAllSounds() { return uSounds.elements(); } /** * Appends the specified sound to this graphics context's list of sounds. * Adding a sound to the list of sounds implicitly starts the * sound playing. Once a sound is finished playing, it can be * restarted by setting the sound's enable flag to true. * The scheduling region of all sounds in the list is ignored * for immediate-mode rendering. * @param sound the sound to add * @exception IllegalSharingException if the Sound node * is part of or is subsequently made part of a live scene graph. * @exception NullPointerException if the Sound object is null. */ public void addSound(Sound sound) { if (sound == null) { throw new NullPointerException(J3dI18N.getString("GraphicsContext3D17")); } if (sound.isLive()) { throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D23")); } uSounds.add(sound); if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active) || (Thread.currentThread() == canvas3d.screen.renderer)) { doAddSound(sound); } else if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.ADD_SOUND, sound, null); } else { sendRenderMessage(true, GraphicsContext3D.ADD_SOUND, sound, null); } } void doAddSound(Sound sound) { ((SoundRetained) (sound.retained)).setInImmCtx(true); updateSoundState((SoundRetained) (sound.retained)); this.sounds.add(sound); this.soundsChanged = true; sendSoundMessage(GraphicsContext3D.ADD_SOUND, sound, null); } /** * Retrieves the current number of sounds in this graphics context. * @return the current number of sounds */ public int numSounds() { return uSounds.size(); } SoundScheduler getSoundScheduler() { if (canvas3d != null && canvas3d.view != null) return canvas3d.view.soundScheduler; // could be null as well else return (SoundScheduler) null; } void updateSoundState(SoundRetained sound) { View view = null; if (canvas3d != null) view = canvas3d.view; // Make sure that: // . Current view is not null // . The sound scheduler running (reference to it is not null) if (view != null) { SoundScheduler soundScheduler = getSoundScheduler(); if (soundScheduler == null) { // XXXX: Re-implement // start up SoundScheduler since it hasn't already been started } } // Update sound fields related to transforms if (sound instanceof ConeSoundRetained) { ConeSoundRetained cs = (ConeSoundRetained) sound; this.modelTransform.transform(cs.direction, cs.xformDirection); cs.xformDirection.normalize(); this.modelTransform.transform(cs.position, cs.xformPosition); // XXXX (Question) Is drawTranform equivalent to Vworld-to-Local? cs.trans.setWithLock(drawTransform); } else if (sound instanceof PointSoundRetained) { PointSoundRetained ps = (PointSoundRetained) sound; this.modelTransform.transform(ps.position, ps.xformPosition); // XXXX (Question) Is drawTranform equivalent to Vworld-to-Local? ps.trans.setWithLock(drawTransform); } } /** * Retrieves the sound playing flag. * @param index which sound * @return flag denoting if sound is currently playing */ public boolean isSoundPlaying(int index) { Sound sound; // uSounds isPlaying field is NOT updated, sounds elements are used sound = this.sounds.get(index); return sound.isPlaying(); } /** * Sets the current AuralAttributes object to the specified * AuralAttributes component object. * This means that the application may modify individual * audio attributes by using the appropriate methods in * the Aural-Attributes object. * @param attributes the new AuralAttributes object */ public void setAuralAttributes(AuralAttributes attributes) { uAuralAttributes = attributes; if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active) || (Thread.currentThread() == canvas3d.screen.renderer)) { doSetAuralAttributes(attributes); } else if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.SET_AURAL_ATTRIBUTES, attributes, null); } else { sendRenderMessage(true, GraphicsContext3D.SET_AURAL_ATTRIBUTES, attributes, null); } } void doSetAuralAttributes(AuralAttributes attributes) { this.auralAttributes = attributes; sendSoundMessage(GraphicsContext3D.SET_AURAL_ATTRIBUTES, attributes, null); } /** * Retrieves the current AuralAttributes component object. * @return the current AuralAttributes object */ public AuralAttributes getAuralAttributes() { return uAuralAttributes; } /** * Sets a flag that specifies whether the double buffering and * stereo mode from the Canvas3D are overridden. When set to * true, this attribute enables the * <code>frontBufferRendering</code> and <code>stereoMode</code> * attributes. * * @param bufferOverride the new buffer override flag * * @see #setFrontBufferRendering * @see #setStereoMode * * @since Java 3D 1.2 */ public void setBufferOverride(boolean bufferOverride) { uBufferOverride = bufferOverride; if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active) || (Thread.currentThread() == canvas3d.screen.renderer)) { doSetBufferOverride(bufferOverride); } else if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.SET_BUFFER_OVERRIDE, new Boolean(bufferOverride), null); } else { sendRenderMessage(true, GraphicsContext3D.SET_BUFFER_OVERRIDE, new Boolean(bufferOverride), null); } } void doSetBufferOverride(boolean bufferOverride) { if (bufferOverride != this.bufferOverride) { this.bufferOverride = bufferOverride; dirtyMask |= BUFFER_MODE; } } /** * Returns the current buffer override flag. * @return true if buffer override is enabled; otherwise, * false is returned * * @since Java 3D 1.2 */ public boolean getBufferOverride() { return uBufferOverride; } /** * Sets a flag that enables or disables immediate mode rendering * into the front buffer of a double buffered Canvas3D. * This attribute is only used when the * <code>bufferOverride</code> flag is enabled. * <p> * Note that this attribute has no effect if double buffering * is disabled or is not available on the Canvas3D. * * @param frontBufferRendering the new front buffer rendering flag * * @see #setBufferOverride * * @since Java 3D 1.2 */ public void setFrontBufferRendering(boolean frontBufferRendering) { uFrontBufferRendering = frontBufferRendering; if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active) || (Thread.currentThread() == canvas3d.screen.renderer)) { doSetFrontBufferRendering(frontBufferRendering); } else if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.SET_FRONT_BUFFER_RENDERING, new Boolean(frontBufferRendering), null); } else { sendRenderMessage(true, GraphicsContext3D.SET_FRONT_BUFFER_RENDERING, new Boolean(frontBufferRendering), null); } } void doSetFrontBufferRendering(boolean frontBufferRendering) { if (frontBufferRendering != this.frontBufferRendering) { this.frontBufferRendering = frontBufferRendering; dirtyMask |= BUFFER_MODE; } } /** * Returns the current front buffer rendering flag. * @return true if front buffer rendering is enabled; otherwise, * false is returned * * @since Java 3D 1.2 */ public boolean getFrontBufferRendering() { return uFrontBufferRendering; } /** * Sets the stereo mode for immediate mode rendering. The * parameter specifies which stereo buffer or buffers is rendered * into. This attribute is only used when the * <code>bufferOverride</code> flag is enabled. * <ul> * <li> * <code>STEREO_LEFT</code> specifies that rendering is done into * the left eye. * </li> * <li> * <code>STEREO_RIGHT</code> specifies that rendering is done into * the right eye. * </li> * <li> * <code>STEREO_BOTH</code> specifies that rendering is done into * both eyes. This is the default. * </li> * </ul> * * <p> * Note that this attribute has no effect if stereo is disabled or * is not available on the Canvas3D. * * @param stereoMode the new stereo mode * * @see #setBufferOverride * * @since Java 3D 1.2 */ public void setStereoMode(int stereoMode) { uStereoMode = stereoMode; if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active) || (Thread.currentThread() == canvas3d.screen.renderer)) { doSetStereoMode(stereoMode); } else if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.SET_STEREO_MODE, stereoModes[stereoMode], null); } else { sendRenderMessage(true, GraphicsContext3D.SET_STEREO_MODE, stereoModes[stereoMode], null); } } void doSetStereoMode(int stereoMode) { if (stereoMode != this.stereoMode) { this.stereoMode = stereoMode; dirtyMask |= BUFFER_MODE; } } /** * Returns the current stereo mode. * @return the stereo mode, one of <code>STEREO_LEFT</code>, * <code>STEREO_RIGHT</code>, or <code>STEREO_BOTH</code>. * * @since Java 3D 1.2 */ public int getStereoMode() { return uStereoMode; } // // Methods to draw graphics objects // /** * Clear the Canvas3D to the color or image specified by the * current background node. */ public void clear() { if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active)) { return; } else if (Thread.currentThread() == canvas3d.screen.renderer) { doClear(); } else if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.CLEAR, null, null); } else { sendRenderMessage(true, GraphicsContext3D.CLEAR, null, null); } } void doClear() { if (!canvas3d.firstPaintCalled) return; RenderBin rb = canvas3d.view.renderBin; BackgroundRetained back = null; if (this.background != null) back = (BackgroundRetained) this.background.retained; else back = this.black; // XXXX: This should ideally be done by the renderer (or by the // canvas itself) when the canvas is first added to a view. /* if ((canvas3d.screen.renderer != null) && (canvas3d.screen.renderer.renderBin == null)) canvas3d.screen.renderer.renderBin = rb; */ // If we are in pure immediate mode, update the view cache if (!canvas3d.isRunning) updateViewCache(rb); // We need to catch NullPointerException when the dsi // gets yanked from us during a remove. try { // Issue 78 - need to get the drawingSurface info every // frame; this is necessary since the HDC (window ID) // on Windows can become invalidated without our // being notified! if (!canvas3d.offScreen) { canvas3d.drawingSurfaceObject.getDrawingSurfaceObjectInfo(); } if (canvas3d.drawingSurfaceObject.renderLock()) { // XXXX : Fix texture /* if (canvas3d.useSharedCtx) { if (canvas3d.screen.renderer.sharedCtx == 0) { synchronized (VirtualUniverse.mc.contextCreationLock) { canvas3d.screen.renderer.sharedCtx = canvas3d.createNewContext( canvas3d.screen.display, canvas3d.window, 0, true, canvas3d.offScreen); canvas3d.screen.renderer.sharedCtxTimeStamp = VirtualUniverse.mc.getContextTimeStamp(); canvas3d.screen.renderer.needToRebuildDisplayList = true; } } } */ if (canvas3d.ctx == null) { synchronized (VirtualUniverse.mc.contextCreationLock) { canvas3d.ctx = canvas3d.createNewContext(null, false); if (canvas3d.ctx == null) { canvas3d.drawingSurfaceObject.unLock(); return; } canvas3d.ctxTimeStamp = VirtualUniverse.mc.getContextTimeStamp(); canvas3d.screen.renderer.listOfCtxs.add(canvas3d.ctx); canvas3d.screen.renderer.listOfCanvases.add(canvas3d); if (canvas3d.graphics2D != null) { canvas3d.graphics2D.init(); } // enable separate specular color canvas3d.enableSeparateSpecularColor(); } // create the cache texture state in canvas // for state download checking purpose if (canvas3d.texUnitState == null) { canvas3d.createTexUnitState(); } canvas3d.drawingSurfaceObject.contextValidated(); canvas3d.screen.renderer.currentCtx = canvas3d.ctx; canvas3d.screen.renderer.currentDrawable = canvas3d.drawable; initializeState(); canvas3d.ctxChanged = true; canvas3d.canvasDirty = 0xffff; // Update Appearance updateState(rb, RenderMolecule.SURFACE); canvas3d.currentLights = new LightRetained[canvas3d.getNumCtxLights(canvas3d.ctx)]; for (int j = 0; j < canvas3d.currentLights.length; j++) { canvas3d.currentLights[j] = null; } } canvas3d.makeCtxCurrent(); if ((dirtyMask & BUFFER_MODE) != 0) { if (bufferOverride) { canvas3d.setRenderMode(canvas3d.ctx, stereoMode, canvas3d.useDoubleBuffer && !frontBufferRendering); } else { if (!canvas3d.isRunning) { canvas3d.setRenderMode(canvas3d.ctx, Canvas3D.FIELD_ALL, canvas3d.useDoubleBuffer); } } dirtyMask &= ~BUFFER_MODE; } Dimension size = canvas3d.getSize(); int winWidth = size.width; int winHeight = size.height; boolean isByRefBackgroundImage = false; if (back.image != null) { if (back.image.isByReference()) { back.image.geomLock.getLock(); isByRefBackgroundImage = true; } back.image.evaluateExtensions(canvas3d); } canvas3d.clear(back, winWidth, winHeight); if (isByRefBackgroundImage) { back.image.geomLock.unLock(); } // Set the viewport and view matrices if (!canvas3d.isRunning) { CanvasViewCache cvCache = canvas3d.canvasViewCache; canvas3d.setViewport(canvas3d.ctx, 0, 0, cvCache.getCanvasWidth(), cvCache.getCanvasHeight()); if (bufferOverride && (stereoMode == STEREO_RIGHT)) { canvas3d.setProjectionMatrix(canvas3d.ctx, cvCache.getRightProjection()); canvas3d.setModelViewMatrix(canvas3d.ctx, cvCache.getRightVpcToEc().mat, rb.vworldToVpc); } else { canvas3d.setProjectionMatrix(canvas3d.ctx, cvCache.getLeftProjection()); canvas3d.setModelViewMatrix(canvas3d.ctx, cvCache.getLeftVpcToEc().mat, rb.vworldToVpc); } } canvas3d.drawingSurfaceObject.unLock(); } } catch (NullPointerException ne) { canvas3d.drawingSurfaceObject.unLock(); throw ne; } } // Method to update compTransform. private void computeCompositeTransform() { ViewPlatform vp; if ((canvas3d == null) || (canvas3d.view == null) || (((vp = canvas3d.view.getViewPlatform()) == null)) || (((ViewPlatformRetained) (vp.retained)) == null)) { compTransform.set(modelTransform); return; } ViewPlatformRetained vpR = (ViewPlatformRetained) vp.retained; if ((vpR == null) || (vpR.locale == null)) { compTransform.set(modelTransform); return; } HiResCoord localeHiRes = vpR.locale.hiRes; if (localeHiRes.equals(hiRes)) { compTransform.set(modelTransform); } else { Transform3D trans = new Transform3D(); Vector3d localeTrans = new Vector3d(); localeHiRes.difference(hiRes, localeTrans); trans.setTranslation(localeTrans); compTransform.mul(trans, modelTransform); } } // Method to update the view cache in pure immediate mode private void updateViewCache(RenderBin rb) { ViewPlatform vp = canvas3d.view.getViewPlatform(); if (vp == null) return; ViewPlatformRetained vpR = (ViewPlatformRetained) vp.retained; if (!canvas3d.isRunning) { // in pure immediate mode, notify platform transform change vpR.evaluateInitViewPlatformTransform(); } // rb.setVworldToVpc(vp.getVworldToVpc()); // rb.setVpcToVworld(vp.getVpcToVworld()); // XXXX: Fix this rb.vpcToVworld = vpR.getVpcToVworld(); rb.vworldToVpc = vpR.getVworldToVpc(); canvas3d.updateViewCache(true, null, null, false); } void doDraw(Geometry geometry) { boolean useAlpha; GeometryRetained drawGeo; GeometryArrayRetained geoRetained = null; if (!canvas3d.firstPaintCalled || !visible) { return; } RenderBin rb = canvas3d.view.renderBin; if (canvas3d.ctx == null) { // Force an initial clear if one has not yet been done doClear(); } if (J3dDebug.devPhase && J3dDebug.debug) { J3dDebug.doAssert(canvas3d.ctx != null, "canvas3d.ctx != null"); } // We need to catch NullPointerException when the dsi // gets yanked from us during a remove. try { if (canvas3d.drawingSurfaceObject.renderLock()) { // Make the context current canvas3d.makeCtxCurrent(); if ((dirtyMask & BUFFER_MODE) != 0) { if (bufferOverride) { canvas3d.setRenderMode(canvas3d.ctx, stereoMode, canvas3d.useDoubleBuffer && !frontBufferRendering); } else { canvas3d.setRenderMode(canvas3d.ctx, Canvas3D.FIELD_ALL, canvas3d.useDoubleBuffer); } dirtyMask &= ~BUFFER_MODE; } CanvasViewCache cvCache = canvas3d.canvasViewCache; // vpcToEc = cvCache.getLeftVpcToEc(); if (bufferOverride) { switch (stereoMode) { case STEREO_RIGHT: vpcToEc = cvCache.getRightVpcToEc(); // XXXX: move this under check for // (dirtyMask & BUFFER_MODE) above after testing // PureImmediate mode canvas3d.setProjectionMatrix(canvas3d.ctx, cvCache.getRightProjection()); break; case STEREO_LEFT: case STEREO_BOTH: default: vpcToEc = cvCache.getLeftVpcToEc(); // XXXX: move this under check for // (dirtyMask & BUFFER_MODE) above after testing // PureImmediate mode canvas3d.setProjectionMatrix(canvas3d.ctx, cvCache.getLeftProjection()); } } else if (!canvas3d.isRunning || // vpcToEc is not set in the first frame // of preRender() callback (canvas3d.vpcToEc == null)) { vpcToEc = cvCache.getLeftVpcToEc(); } else { vpcToEc = canvas3d.vpcToEc; } // referred by RenderQueue.updateState // canvas3d.screen.renderer.vpcToEc = vpcToEc; // rb.updateState(canvas3d.screen.renderer.rId, ro, canvas3d, true); // this.drawTransform.mul(rb.vworldToVpc, // this.compTransform); boolean isNonUniformScale = !drawTransform.isCongruent(); int geometryType = 0; switch (((GeometryRetained) geometry.retained).geoType) { case GeometryRetained.GEO_TYPE_POINT_SET: case GeometryRetained.GEO_TYPE_INDEXED_POINT_SET: geometryType = RenderMolecule.POINT; break; case GeometryRetained.GEO_TYPE_LINE_SET: case GeometryRetained.GEO_TYPE_LINE_STRIP_SET: case GeometryRetained.GEO_TYPE_INDEXED_LINE_SET: case GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET: geometryType = RenderMolecule.LINE; break; case GeometryRetained.GEO_TYPE_RASTER: geometryType = RenderMolecule.RASTER; break; case GeometryRetained.GEO_TYPE_COMPRESSED: geometryType = RenderMolecule.COMPRESSED; switch (((CompressedGeometryRetained) geometry.retained).getBufferType()) { case CompressedGeometryHeader.POINT_BUFFER: geometryType |= RenderMolecule.POINT; break; case CompressedGeometryHeader.LINE_BUFFER: geometryType |= RenderMolecule.LINE; break; default: case CompressedGeometryHeader.TRIANGLE_BUFFER: geometryType |= RenderMolecule.SURFACE; break; } break; default: geometryType = RenderMolecule.SURFACE; break; } useAlpha = updateState(rb, geometryType); canvas3d.setModelViewMatrix(canvas3d.ctx, vpcToEc.mat, rb.vworldToVpc); updateLightAndFog(); updateModelClip(rb.vworldToVpc); this.drawTransform.mul(rb.vworldToVpc, this.compTransform); canvas3d.setModelViewMatrix(canvas3d.ctx, vpcToEc.mat, this.drawTransform); if (geometry.retained instanceof GeometryArrayRetained) { geoRetained = (GeometryArrayRetained) geometry.retained; geoRetained.geomLock.getLock(); // If the geometry is by refernence, then see if we are using alpha // and that there is no global alpha sun extension defined .. if (((geoRetained.vertexFormat & GeometryArray.BY_REFERENCE) != 0) && (geoRetained.c4fAllocated == 0) && ((geoRetained.vertexFormat & GeometryArray.COLOR) != 0) && useAlpha) { if ((geoRetained.vertexFormat & GeometryArray.INTERLEAVED) != 0) { geoRetained.setupMirrorInterleavedColorPointer(true); } else { geoRetained.setupMirrorColorPointer( (geoRetained.vertexType & GeometryArrayRetained.COLOR_DEFINED), true); } } if ((geometry.retained instanceof IndexedGeometryArrayRetained) && ((((GeometryArrayRetained) geometry.retained).vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0)) { if (geoRetained.dirtyFlag != 0) { geoRetained.mirrorGeometry = ((IndexedGeometryArrayRetained) geoRetained) .cloneNonIndexedGeometry(); // Change the source geometry dirtyFlag // drawGeo.execute() will change the // destination geometry dirtyFlag only. geoRetained.dirtyFlag = 0; } drawGeo = (GeometryRetained) geoRetained.mirrorGeometry; } else { drawGeo = geoRetained; } geoRetained.setVertexFormat(false, ignoreVertexColors, canvas3d.ctx); } else if (geometry.retained instanceof Text3DRetained) { ((Text3DRetained) geometry.retained).setModelViewMatrix(vpcToEc, this.drawTransform); drawGeo = (GeometryRetained) geometry.retained; } else if (geometry.retained instanceof RasterRetained) { ImageComponent2DRetained img = ((RasterRetained) geometry.retained).image; if (img != null) { if (img.isByReference()) { img.geomLock.getLock(); img.evaluateExtensions(canvas3d); img.geomLock.unLock(); } else { img.evaluateExtensions(canvas3d); } } drawGeo = (GeometryRetained) geometry.retained; } else { drawGeo = (GeometryRetained) geometry.retained; } drawGeo.execute(canvas3d, null, isNonUniformScale, false, alpha, canvas3d.screen.screen, ignoreVertexColors); if (geoRetained != null) { geoRetained.geomLock.unLock(); } canvas3d.drawingSurfaceObject.unLock(); } } catch (NullPointerException ne) { canvas3d.drawingSurfaceObject.unLock(); throw ne; } } /** * Draw the specified Geometry component object. * @param geometry the Geometry object to draw. * * @exception IllegalSharingException if the specified geometry is a * Raster that refers to an ImageComponent2D that is being used by a * Canvas3D as an off-screen buffer. */ public void draw(Geometry geometry) { // do illegalSharing check if ((geometry != null) && (geometry instanceof Raster)) { RasterRetained rasRetained = (RasterRetained) geometry.retained; ImageComponent2D image = rasRetained.getImage(); if (image != null) { ImageComponentRetained imageRetained = (ImageComponentRetained) image.retained; // Do illegal sharing check if (imageRetained.getUsedByOffScreen()) { throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D32")); } } } if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active)) { return; } else if (Thread.currentThread() == canvas3d.screen.renderer) { doDraw(geometry); } else { if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.DRAW, geometry, null); } else { sendRenderMessage(true, GraphicsContext3D.DRAW, geometry, null); } } } /** * Draw the specified Shape3D leaf node object. This is * a convenience method that is identical to calling the * setAppearance(Appearance) and draw(Geometry) methods * passing the appearance and geometry component objects of * the specified shape node as arguments. * * @param shape the Shape3D node containing the Appearance component * object to set and Geometry component object to draw * * @exception IllegalSharingException if the Shape3D node * is part of or is subsequently made part of a live scene graph. * * @exception IllegalSharingException if the Shape3D node's Appearance * refers to an ImageComponent2D that is being used by a * Canvas3D as an off-screen buffer. */ public void draw(Shape3D shape) { if (shape.isLive()) { throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D26")); } ((Shape3DRetained) shape.retained).setInImmCtx(true); setAppearance(shape.getAppearance()); draw(shape.getGeometry()); } /** * Read an image from the frame buffer and copy it into the * ImageComponent and/or DepthComponent * objects referenced by the specified Raster object. * All parameters of the Raster object and the component ImageComponent * and/or DepthComponentImage objects must be set to the desired values * prior to calling this method. These values determine the location, * size, and format of the pixel data that is read. * This method calls <code>flush(true)</code> prior to reading the * frame buffer. * * @param raster the Raster object used to read the * contents of the frame buffer * * @exception IllegalArgumentException if the image class of the specified * Raster's ImageComponent2D is <i>not</i> ImageClass.BUFFERED_IMAGE. * * @exception IllegalArgumentException if the specified Raster's * ImageComponent2D is in by-reference mode and its * RenderedImage is null. * * @exception IllegalArgumentException if the the Raster's * ImageComponent2D format * is <i>not</i> a 3-component format (e.g., FORMAT_RGB) * or a 4-component format (e.g., FORMAT_RGBA). * * @exception IllegalSharingException if the Raster object is * part of a live scene graph, or if the Raster's ImageComponent2D is * part of a live scene graph. * * @exception IllegalSharingException if the Raster's ImageComponent2D is * being used by an immediate mode context, or by a Canvas3D as * an off-screen buffer. * * @see #flush * @see ImageComponent * @see DepthComponent */ public void readRaster(Raster raster) { if ((raster != null) && raster.isLive()) { ImageComponent2D image = raster.getImage(); if (image != null) { ImageComponent2DRetained imageRetained = (ImageComponent2DRetained) image.retained; if (image.getImageClass() != ImageComponent.ImageClass.BUFFERED_IMAGE) { throw new IllegalArgumentException(J3dI18N.getString("GraphicsContext3D33")); } if (image.isByReference() && (image.getImage() == null)) { throw new IllegalArgumentException(J3dI18N.getString("GraphicsContext3D34")); } if (imageRetained.getNumberOfComponents() < 3) { throw new IllegalArgumentException(J3dI18N.getString("GraphicsContext3D35")); } if (image.isLive()) { throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D36")); } if (imageRetained.getInImmCtx() || imageRetained.getUsedByOffScreen()) { throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D37")); } } } if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active)) { return; } else if (Thread.currentThread() == canvas3d.screen.renderer) { doReadRaster(raster); } else if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { readRasterReady = false; sendRenderMessage(false, GraphicsContext3D.READ_RASTER, raster, null); while (!readRasterReady) { MasterControl.threadYield(); } } else { // call from user thread readRasterReady = false; sendRenderMessage(true, GraphicsContext3D.READ_RASTER, raster, null); while (!readRasterReady) { MasterControl.threadYield(); } } } void doReadRaster(Raster raster) { if (!canvas3d.firstPaintCalled) { readRasterReady = true; return; } RasterRetained ras = (RasterRetained) raster.retained; Dimension canvasSize = canvas3d.getSize(); Dimension rasterSize = new Dimension(); ImageComponent2DRetained image = ras.image; int format = 0; // Not use in case of DepthComponent read if (canvas3d.ctx == null) { // Force an initial clear if one has not yet been done doClear(); } if (J3dDebug.devPhase && J3dDebug.debug) { J3dDebug.doAssert(canvas3d.ctx != null, "canvas3d.ctx != null"); } ras.getSize(rasterSize); // allocate read buffer space if ((ras.type & Raster.RASTER_COLOR) != 0) { if ((rasterSize.width > ras.image.width) || (rasterSize.height > ras.image.height)) { throw new RuntimeException(J3dI18N.getString("GraphicsContext3D27")); } } if ((ras.type & Raster.RASTER_DEPTH) != 0) { int size = ras.depthComponent.height * ras.depthComponent.width; if (ras.depthComponent.type == DepthComponentRetained.DEPTH_COMPONENT_TYPE_FLOAT) { if (floatBuffer.length < size) floatBuffer = new float[size]; } else { // type INT or NATIVE if (intBuffer.length < size) intBuffer = new int[size]; } if ((rasterSize.width > ras.depthComponent.width) || (rasterSize.height > ras.depthComponent.height)) { throw new RuntimeException(J3dI18N.getString("GraphicsContext3D28")); } } if ((ras.type & Raster.RASTER_COLOR) != 0) { // If by reference, check if a copy needs to be made // and also evaluate the storedFormat .. if (image.isByReference()) { image.geomLock.getLock(); image.evaluateExtensions(canvas3d); image.geomLock.unLock(); } else { // If image has a null buffer ( BufferedImage) if (image.imageData == null) { image.createBlankImageData(); } // Check for possible format conversion in imageData else { // Format convert imageData if format is unsupported. image.evaluateExtensions(canvas3d); } } } // We need to catch NullPointerException when the dsi // gets yanked from us during a remove. try { if (canvas3d.drawingSurfaceObject.renderLock()) { // Make the context current and read the raster information canvas3d.makeCtxCurrent(); canvas3d.syncRender(canvas3d.ctx, true); Point rasterSrcOffset = new Point(); ras.getSrcOffset(rasterSrcOffset); int depthType = 0; Object depthBuffer = null; if (ras.depthComponent != null) { depthType = ras.depthComponent.type; if (depthType == DepthComponentRetained.DEPTH_COMPONENT_TYPE_FLOAT) depthBuffer = floatBuffer; else depthBuffer = intBuffer; } int imageDataType = 0; int imageFormatType = 0; Object imageBuffer = null; if ((ras.type & Raster.RASTER_COLOR) != 0) { imageDataType = image.getImageDataTypeIntValue(); imageFormatType = image.getImageFormatTypeIntValue(false); imageBuffer = image.imageData.get(); } Pipeline.getPipeline().readRaster(canvas3d.ctx, ras.type, rasterSrcOffset.x, rasterSrcOffset.y, rasterSize.width, rasterSize.height, canvasSize.height, imageDataType, imageFormatType, imageBuffer, depthType, depthBuffer); canvas3d.drawingSurfaceObject.unLock(); } } catch (NullPointerException ne) { canvas3d.drawingSurfaceObject.unLock(); throw ne; } if ((ras.type & Raster.RASTER_DEPTH) != 0) { if (ras.depthComponent.type == DepthComponentRetained.DEPTH_COMPONENT_TYPE_FLOAT) ((DepthComponentFloatRetained) ras.depthComponent).retrieveDepth(floatBuffer, rasterSize.width, rasterSize.height); else if (ras.depthComponent.type == DepthComponentRetained.DEPTH_COMPONENT_TYPE_INT) ((DepthComponentIntRetained) ras.depthComponent).retrieveDepth(intBuffer, rasterSize.width, rasterSize.height); else if (ras.depthComponent.type == DepthComponentRetained.DEPTH_COMPONENT_TYPE_NATIVE) ((DepthComponentNativeRetained) ras.depthComponent).retrieveDepth(intBuffer, rasterSize.width, rasterSize.height); } readRasterReady = true; } /** * Flushes all previously executed rendering operations to the * drawing buffer for this 3D graphics context. * * @param wait flag indicating whether or not to wait for the * rendering to be complete before returning from this call. * * @since Java 3D 1.2 */ public void flush(boolean wait) { if ((canvas3d.view == null) || (canvas3d.view.universe == null) || (!canvas3d.view.active) || (Thread.currentThread() == canvas3d.screen.renderer)) { doFlush(wait); } else { Boolean waitArg = (wait ? Boolean.TRUE : Boolean.FALSE); if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) { sendRenderMessage(false, GraphicsContext3D.FLUSH, waitArg, null); } else { sendRenderMessage(true, GraphicsContext3D.FLUSH, waitArg, null); } // Issue 131: AutomaticOffscreen canvases must be treated as onscreen ones. if (wait && canvas3d.active && canvas3d.isRunningStatus && !canvas3d.manualRendering) { // No need to wait if renderer thread is not schedule runMonitor(J3dThread.WAIT); } } } void doFlush(boolean wait) { try { if (canvas3d.drawingSurfaceObject.renderLock()) { canvas3d.syncRender(canvas3d.ctx, wait); canvas3d.drawingSurfaceObject.unLock(); if (wait) { runMonitor(J3dThread.NOTIFY); } } } catch (NullPointerException ne) { canvas3d.drawingSurfaceObject.unLock(); throw ne; } } void updateLightAndFog() { int enableMask = 0; int i; sceneAmbient.x = 0.0f; sceneAmbient.y = 0.0f; sceneAmbient.z = 0.0f; int n = 0; int nLight = lights.size(); ; for (i = 0; i < nLight; i++) { LightRetained lt = (LightRetained) (lights.get(i)).retained; if (lt instanceof AmbientLightRetained) { sceneAmbient.x += lt.color.x; sceneAmbient.y += lt.color.y; sceneAmbient.z += lt.color.z; continue; } lt.update(canvas3d.ctx, n, canvas3d.canvasViewCache.getVworldToCoexistenceScale()); if (lt.lightOn) enableMask |= (1 << n); n++; } if (sceneAmbient.x > 1.0f) { sceneAmbient.x = 1.0f; } if (sceneAmbient.y > 1.0f) { sceneAmbient.y = 1.0f; } if (sceneAmbient.z > 1.0f) { sceneAmbient.z = 1.0f; } canvas3d.setSceneAmbient(canvas3d.ctx, sceneAmbient.x, sceneAmbient.y, sceneAmbient.z); canvas3d.canvasDirty |= Canvas3D.AMBIENTLIGHT_DIRTY; canvas3d.sceneAmbient.set(sceneAmbient); if (canvas3d.enableMask != enableMask) { canvas3d.canvasDirty |= Canvas3D.LIGHTENABLES_DIRTY; // XXXX: 32 => renderBin.maxLights canvas3d.setLightEnables(canvas3d.ctx, enableMask, 32); canvas3d.enableMask = enableMask; } // Force LightBin.updateAttributes and EnvironmentSet.updateAttributes // to use the within frame case. canvas3d.lightBin = null; canvas3d.environmentSet = null; if (fog != null) { if (fog.retained != canvas3d.fog) { ((FogRetained) fog.retained).update(canvas3d.ctx, canvas3d.canvasViewCache.getVworldToCoexistenceScale()); canvas3d.fog = (FogRetained) fog.retained; canvas3d.canvasDirty |= Canvas3D.FOG_DIRTY; } } else { // Turn off fog if (canvas3d.fog != null) { canvas3d.setFogEnableFlag(canvas3d.ctx, false); canvas3d.fog = null; canvas3d.canvasDirty |= Canvas3D.FOG_DIRTY; } } } void updateModelClip(Transform3D vworldToVpc) { if (modelClip != null) { int enableMask = 0; for (int i = 0; i < 6; i++) { if (((ModelClipRetained) modelClip.retained).enables[i]) enableMask |= 1 << i; } // planes are already transformed to eye coordinates // in immediate mode if (enableMask != 0) { this.drawTransform.mul(vworldToVpc, this.modelClipTransform); canvas3d.setModelViewMatrix(canvas3d.ctx, vpcToEc.mat, this.drawTransform); } ((ModelClipRetained) modelClip.retained).update(canvas3d.ctx, enableMask, this.drawTransform); canvas3d.canvasDirty |= Canvas3D.MODELCLIP_DIRTY; canvas3d.modelClip = (ModelClipRetained) modelClip.retained; } else { if (canvas3d.modelClip != null) { canvas3d.disableModelClip(canvas3d.ctx); canvas3d.modelClip = null; canvas3d.canvasDirty |= Canvas3D.MODELCLIP_DIRTY; } } // Force EnvironmentSet.updateAttributes to use the within frame case. canvas3d.environmentSet = null; } boolean updateState(RenderBin rb, int geometryType) { boolean useAlpha = false; numActiveTexUnit = 0; lastActiveTexUnitIndex = 0; // Update Appearance if (appearance != null) { AppearanceRetained app = (AppearanceRetained) appearance.retained; // If the material is not null then check if the one in the canvas // is equivalent to the one being sent down. If Yes, do nothing // Otherwise, cache the sent down material and mark the canvas // dirty flag so that the compiled/compiled-retained rendering // catches the change // if material != null, we will need to load the material // parameter again, because the apps could have changed // the material parameter if (app.material != null) { app.material.updateNative(canvas3d.ctx, red, green, blue, alpha, enableLighting); canvas3d.material = app.material; canvas3d.canvasDirty |= Canvas3D.MATERIAL_DIRTY; } else { if (canvas3d.material != null) { canvas3d.updateMaterial(canvas3d.ctx, red, green, blue, alpha); canvas3d.material = null; canvas3d.canvasDirty |= Canvas3D.MATERIAL_DIRTY; } } // Set flag indicating whether we are using shaders boolean useShaders = false; if (app instanceof ShaderAppearanceRetained) { ShaderProgramRetained spR = ((ShaderAppearanceRetained) app).shaderProgram; if (spR != null) { spR.updateNative(canvas3d, true); ShaderAttributeSetRetained sasR = ((ShaderAppearanceRetained) app).shaderAttributeSet; if (sasR != null) { sasR.updateNative(canvas3d, spR); } canvas3d.shaderProgram = spR; useShaders = true; } } else if (canvas3d.shaderProgram != null) { canvas3d.shaderProgram.updateNative(canvas3d, false); canvas3d.shaderProgram = null; useShaders = false; } // Set the number of available texture units; this depends on // whether or not shaders are being used. int availableTextureUnits = useShaders ? canvas3d.maxTextureImageUnits : canvas3d.maxTextureUnits; int prevNumActiveTexUnit = canvas3d.getNumActiveTexUnit(); // Get the number of active texture units. // Note that this total number now includes disabled units. if (app.texUnitState != null) { TextureUnitStateRetained tus; for (int i = 0; i < app.texUnitState.length; i++) { tus = app.texUnitState[i]; if (tus != null && tus.isTextureEnabled()) { lastActiveTexUnitIndex = i; numActiveTexUnit = i + 1; if (tus.texAttrs != null) { useAlpha = useAlpha || (tus.texAttrs.textureMode == TextureAttributes.BLEND); } } } if (numActiveTexUnit <= availableTextureUnits) { // Normal, single-pass rendering case // update all active texture unit states for (int i = 0; i < app.texUnitState.length; i++) { if (i >= availableTextureUnits) { // This can happen if there are disabled units at // the end of the array break; } if ((app.texUnitState[i] != null) && app.texUnitState[i].isTextureEnabled()) { app.texUnitState[i].updateNative(i, canvas3d, false, false); } else { canvas3d.resetTexture(canvas3d.ctx, i); } } // reset the remaining texture units for (int i = app.texUnitState.length; i < prevNumActiveTexUnit; i++) { canvas3d.resetTexture(canvas3d.ctx, i); } // set the number active texture unit in Canvas3D canvas3d.setNumActiveTexUnit(numActiveTexUnit); } else { // Exceeded limit, disable all the texture units for (int i = 0; i < prevNumActiveTexUnit; i++) { canvas3d.resetTexture(canvas3d.ctx, i); } canvas3d.setNumActiveTexUnit(0); } // set the active texture unit back to 0 canvas3d.activeTextureUnit(canvas3d.ctx, 0); } else { // if texUnitState is null, let's disable // all texture units first if (canvas3d.multiTexAccelerated) { if (canvas3d.texUnitState != null) { for (int i = 0; i < prevNumActiveTexUnit; i++) { TextureUnitStateRetained tur = canvas3d.texUnitState[i]; if ((tur != null) && (tur.texture != null)) { canvas3d.resetTexture(canvas3d.ctx, i); canvas3d.texUnitState[i].texture = null; } } } // set the active texture unit back to 0 canvas3d.activeTextureUnit(canvas3d.ctx, 0); } if ((canvas3d.texUnitState != null) && (canvas3d.texUnitState[0] != null) && (canvas3d.texUnitState[0].texture != app.texture)) { // If the image is by reference, check if the image // should be processed if (app.texture != null) { app.texture.updateNative(canvas3d); canvas3d.canvasDirty |= Canvas3D.TEXTUREBIN_DIRTY | Canvas3D.TEXTUREATTRIBUTES_DIRTY; numActiveTexUnit = 1; lastActiveTexUnitIndex = 0; } else { numActiveTexUnit = 0; canvas3d.resetTexture(canvas3d.ctx, -1); canvas3d.canvasDirty |= Canvas3D.TEXTUREBIN_DIRTY | Canvas3D.TEXTUREATTRIBUTES_DIRTY; } canvas3d.texUnitState[0].texture = app.texture; } // set the number active texture unit in Canvas3D canvas3d.setNumActiveTexUnit(numActiveTexUnit); if (app.texCoordGeneration != null) { app.texCoordGeneration.updateNative(canvas3d); canvas3d.canvasDirty |= Canvas3D.TEXTUREBIN_DIRTY | Canvas3D.TEXTUREATTRIBUTES_DIRTY; if ((canvas3d.texUnitState != null) && (canvas3d.texUnitState[0] != null)) { canvas3d.texUnitState[0].texGen = app.texCoordGeneration; } } else { // If the canvas does not alreadt have a null texCoordGeneration // load the default if ((canvas3d.texUnitState != null) && (canvas3d.texUnitState[0] != null) && (canvas3d.texUnitState[0].texGen != null)) { canvas3d.resetTexCoordGeneration(canvas3d.ctx); canvas3d.canvasDirty |= Canvas3D.TEXTUREBIN_DIRTY | Canvas3D.TEXTUREATTRIBUTES_DIRTY; canvas3d.texUnitState[0].texGen = app.texCoordGeneration; } } if (app.textureAttributes != null) { if ((canvas3d.texUnitState != null) && (canvas3d.texUnitState[0] != null)) { if (canvas3d.texUnitState[0].texture != null) { app.textureAttributes.updateNative(canvas3d, false, canvas3d.texUnitState[0].texture.format); } else { app.textureAttributes.updateNative(canvas3d, false, Texture.RGBA); } canvas3d.canvasDirty |= Canvas3D.TEXTUREBIN_DIRTY | Canvas3D.TEXTUREATTRIBUTES_DIRTY; canvas3d.texUnitState[0].texAttrs = app.textureAttributes; } } else { // If the canvas does not already have a null texAttribute // load the default if necessary if ((canvas3d.texUnitState != null) && (canvas3d.texUnitState[0] != null) && (canvas3d.texUnitState[0].texAttrs != null)) { canvas3d.resetTextureAttributes(canvas3d.ctx); canvas3d.canvasDirty |= Canvas3D.TEXTUREBIN_DIRTY | Canvas3D.TEXTUREATTRIBUTES_DIRTY; canvas3d.texUnitState[0].texAttrs = null; } } } if (app.coloringAttributes != null) { app.coloringAttributes.updateNative(canvas3d.ctx, dRed, dBlue, dGreen, alpha, enableLighting); canvas3d.canvasDirty |= Canvas3D.COLORINGATTRS_DIRTY; canvas3d.coloringAttributes = app.coloringAttributes; } else { if (canvas3d.coloringAttributes != null) { canvas3d.resetColoringAttributes(canvas3d.ctx, red, green, blue, alpha, enableLighting); canvas3d.canvasDirty |= Canvas3D.COLORINGATTRS_DIRTY; canvas3d.coloringAttributes = null; } } if (app.transparencyAttributes != null) { app.transparencyAttributes.updateNative(canvas3d.ctx, alpha, geometryType, polygonMode, lineAA, pointAA); canvas3d.canvasDirty |= Canvas3D.TRANSPARENCYATTRS_DIRTY; canvas3d.transparency = app.transparencyAttributes; if (!useAlpha) useAlpha = TransparencyAttributesRetained.useAlpha(app.transparencyAttributes); } else { canvas3d.resetTransparency(canvas3d.ctx, geometryType, polygonMode, lineAA, pointAA); canvas3d.canvasDirty |= Canvas3D.TRANSPARENCYATTRS_DIRTY; canvas3d.transparency = null; } if (app.renderingAttributes != null) { ignoreVertexColors = app.renderingAttributes.ignoreVertexColors; app.renderingAttributes.updateNative(canvas3d, canvas3d.depthBufferWriteEnableOverride, canvas3d.depthBufferEnableOverride); canvas3d.canvasDirty |= Canvas3D.ATTRIBUTEBIN_DIRTY | Canvas3D.TEXTUREATTRIBUTES_DIRTY; canvas3d.renderingAttrs = app.renderingAttributes; useAlpha = useAlpha || (app.renderingAttributes.alphaTestFunction != RenderingAttributes.ALWAYS); } else { // If the canvas does not alreadt have a null renderingAttrs // load the default ignoreVertexColors = false; if (canvas3d.renderingAttrs != null) { canvas3d.resetRenderingAttributes(canvas3d.ctx, canvas3d.depthBufferWriteEnableOverride, canvas3d.depthBufferEnableOverride); canvas3d.canvasDirty |= Canvas3D.ATTRIBUTEBIN_DIRTY | Canvas3D.TEXTUREATTRIBUTES_DIRTY; canvas3d.renderingAttrs = null; } } if (app.polygonAttributes != null) { app.polygonAttributes.updateNative(canvas3d.ctx); canvas3d.canvasDirty |= Canvas3D.POLYGONATTRS_DIRTY; canvas3d.polygonAttributes = app.polygonAttributes; } else { // If the canvas does not alreadt have a null polygonAttr // load the default if (canvas3d.polygonAttributes != null) { canvas3d.resetPolygonAttributes(canvas3d.ctx); canvas3d.canvasDirty |= Canvas3D.POLYGONATTRS_DIRTY; canvas3d.polygonAttributes = null; } } if (app.lineAttributes != null) { app.lineAttributes.updateNative(canvas3d.ctx); canvas3d.canvasDirty |= Canvas3D.LINEATTRS_DIRTY; canvas3d.lineAttributes = app.lineAttributes; } else { // If the canvas does not already have a null lineAttr // load the default if (canvas3d.lineAttributes != null) { canvas3d.resetLineAttributes(canvas3d.ctx); canvas3d.canvasDirty |= Canvas3D.LINEATTRS_DIRTY; canvas3d.lineAttributes = null; } } if (app.pointAttributes != null) { app.pointAttributes.updateNative(canvas3d.ctx); canvas3d.canvasDirty |= Canvas3D.POINTATTRS_DIRTY; canvas3d.pointAttributes = app.pointAttributes; } else { // If the canvas does not already have a null pointAttr // load the default if (canvas3d.pointAttributes != null) { canvas3d.resetPointAttributes(canvas3d.ctx); canvas3d.canvasDirty |= Canvas3D.POINTATTRS_DIRTY; canvas3d.pointAttributes = null; } } canvas3d.appearance = app; } else { if (canvas3d.appearance != null) { resetAppearance(); canvas3d.appearance = null; } } return (useAlpha); } void initializeState() { canvas3d.setSceneAmbient(canvas3d.ctx, 0.0f, 0.0f, 0.0f); canvas3d.disableFog(canvas3d.ctx); canvas3d.resetRenderingAttributes(canvas3d.ctx, false, false); if (canvas3d.shaderProgram != null) { canvas3d.shaderProgram.updateNative(canvas3d, false); canvas3d.shaderProgram = null; } // reset the previously enabled texture units int prevNumActiveTexUnit = canvas3d.getNumActiveTexUnit(); if (prevNumActiveTexUnit > 0) { for (int i = 0; i < prevNumActiveTexUnit; i++) { if (canvas3d.texUnitState[i].texture != null) { canvas3d.resetTexture(canvas3d.ctx, i); canvas3d.texUnitState[i].texture = null; } if (canvas3d.texUnitState[i].texAttrs != null) { canvas3d.resetTextureAttributes(canvas3d.ctx); canvas3d.texUnitState[i].texAttrs = null; } if (canvas3d.texUnitState[i].texGen != null) { canvas3d.resetTexCoordGeneration(canvas3d.ctx); canvas3d.texUnitState[i].texGen = null; } canvas3d.texUnitState[i].mirror = null; } canvas3d.setNumActiveTexUnit(0); } canvas3d.resetPolygonAttributes(canvas3d.ctx); canvas3d.resetLineAttributes(canvas3d.ctx); canvas3d.resetPointAttributes(canvas3d.ctx); canvas3d.resetTransparency(canvas3d.ctx, RenderMolecule.SURFACE, PolygonAttributes.POLYGON_FILL, false, false); canvas3d.resetColoringAttributes(canvas3d.ctx, 1.0f, 1.0f, 1.0f, 1.0f, false); canvas3d.updateMaterial(canvas3d.ctx, 1.0f, 1.0f, 1.0f, 1.0f); } void resetAppearance() { //System.err.println("GC3D.resetAppearance ...."); if (canvas3d.material != null) { canvas3d.updateMaterial(canvas3d.ctx, red, green, blue, alpha); canvas3d.material = null; canvas3d.canvasDirty |= Canvas3D.MATERIAL_DIRTY; } if (canvas3d.shaderProgram != null) { canvas3d.shaderProgram.updateNative(canvas3d, false); canvas3d.shaderProgram = null; // ShaderBin doesn't use dirty bit. } // reset the previously enabled texture units int prevNumActiveTexUnit = canvas3d.getNumActiveTexUnit(); if (prevNumActiveTexUnit > 0) { for (int i = 0; i < prevNumActiveTexUnit; i++) { if (canvas3d.texUnitState[i].texture != null) { canvas3d.resetTexture(canvas3d.ctx, i); canvas3d.texUnitState[i].texture = null; } if (canvas3d.texUnitState[i].texAttrs != null) { canvas3d.resetTextureAttributes(canvas3d.ctx); canvas3d.texUnitState[i].texAttrs = null; } if (canvas3d.texUnitState[i].texGen != null) { canvas3d.resetTexCoordGeneration(canvas3d.ctx); canvas3d.texUnitState[i].texGen = null; } canvas3d.texUnitState[i].mirror = null; } canvas3d.canvasDirty |= Canvas3D.TEXTUREBIN_DIRTY | Canvas3D.TEXTUREATTRIBUTES_DIRTY; canvas3d.setNumActiveTexUnit(0); } if (canvas3d.coloringAttributes != null) { canvas3d.resetColoringAttributes(canvas3d.ctx, red, green, blue, alpha, enableLighting); canvas3d.coloringAttributes = null; canvas3d.canvasDirty |= Canvas3D.COLORINGATTRS_DIRTY; } if (canvas3d.transparency != null) { canvas3d.resetTransparency(canvas3d.ctx, RenderMolecule.SURFACE, PolygonAttributes.POLYGON_FILL, lineAA, pointAA); canvas3d.transparency = null; canvas3d.canvasDirty |= Canvas3D.TRANSPARENCYATTRS_DIRTY; } if (canvas3d.renderingAttrs != null) { ignoreVertexColors = false; canvas3d.resetRenderingAttributes(canvas3d.ctx, canvas3d.depthBufferWriteEnableOverride, canvas3d.depthBufferEnableOverride); canvas3d.renderingAttrs = null; canvas3d.canvasDirty |= Canvas3D.ATTRIBUTEBIN_DIRTY | Canvas3D.TEXTUREATTRIBUTES_DIRTY; } if (canvas3d.polygonAttributes != null) { canvas3d.resetPolygonAttributes(canvas3d.ctx); canvas3d.polygonAttributes = null; canvas3d.canvasDirty |= Canvas3D.POLYGONATTRS_DIRTY; } if (canvas3d.lineAttributes != null) { canvas3d.resetLineAttributes(canvas3d.ctx); canvas3d.lineAttributes = null; canvas3d.canvasDirty |= Canvas3D.LINEATTRS_DIRTY; } if (canvas3d.pointAttributes != null) { canvas3d.resetPointAttributes(canvas3d.ctx); canvas3d.pointAttributes = null; canvas3d.canvasDirty |= Canvas3D.POINTATTRS_DIRTY; } } void sendRenderMessage(boolean renderRun, int command, Object arg1, Object arg2) { // send a message to the request renderer J3dMessage renderMessage = new J3dMessage(); renderMessage.threads = J3dThread.RENDER_THREAD; renderMessage.type = J3dMessage.RENDER_IMMEDIATE; renderMessage.universe = null; renderMessage.view = null; renderMessage.args[0] = canvas3d; renderMessage.args[1] = getImmCommand(command); renderMessage.args[2] = arg1; renderMessage.args[3] = arg2; while (!canvas3d.view.inRenderThreadData) { // wait until the renderer thread data in added in // MC:RenderThreadData array ready to receive message MasterControl.threadYield(); } canvas3d.screen.renderer.rendererStructure.addMessage(renderMessage); if (renderRun) { // notify mc that there is work to do VirtualUniverse.mc.sendRunMessage(canvas3d.view, J3dThread.RENDER_THREAD); } else { // notify mc that there is work for the request renderer VirtualUniverse.mc.setWorkForRequestRenderer(); } } void sendSoundMessage(int command, Object arg1, Object arg2) { if ((canvas3d.view == null) || (canvas3d.view.universe == null)) { return; } // send a message to the request sound scheduling J3dMessage soundMessage = new J3dMessage(); soundMessage.threads = J3dThread.SOUND_SCHEDULER; soundMessage.type = J3dMessage.RENDER_IMMEDIATE; soundMessage.universe = canvas3d.view.universe; soundMessage.view = canvas3d.view; soundMessage.args[0] = getImmCommand(command); soundMessage.args[1] = arg1; soundMessage.args[2] = arg2; // notify mc that there is work to do VirtualUniverse.mc.processMessage(soundMessage); } static Integer getImmCommand(int command) { if (commands[command] == null) { commands[command] = new Integer(command); } return commands[command]; } synchronized void runMonitor(int action) { if (action == J3dThread.WAIT) { while (!gcReady) { waiting++; try { wait(); } catch (InterruptedException e) { } waiting--; } gcReady = false; } else { gcReady = true; if (waiting > 0) { notify(); } } } }