/*
* 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:
*
* - Background object: null
* - Fog object: null
* - ModelClip object: null
* - Appearance object: null
* - List of Light objects: empty
* - high-res coordinate: (0, 0, 0)
* - modelTransform: identity
* - AuralAttributes object: null
* - List of Sound objects: empty
* - buffer override: false
* - front buffer rendering: false
* - stereo mode:
STEREO_BOTH
*
*
*
* 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
* flush(boolean)
* method to ensure that the rendering actually happens. The flush
* method is implicitly called in the following cases:
*
*
* - The
readRaster
method calls
* flush(true)
* - The
Canvas3D.swap
method calls
* flush(true)
* - The Java 3D renderer calls
flush(true)
prior to
* swapping the buffer for a double buffered on-screen Canvas3D
* - The Java 3D renderer calls
flush(true)
prior to
* copying into the off-screen buffer of an off-screen Canvas3D
* - The Java 3D renderer calls
flush(false)
after
* calling the preRender, renderField, postRender, and postSwap
* Canvas3D callback methods.
*
*
*
* 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 uLights = new Vector();
private HiResCoord uHiRes = new HiResCoord();
private Vector uSounds = new Vector();
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 lights = new Vector();
// 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 sounds = new Vector();
// 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 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 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
* frontBufferRendering
and stereoMode
* 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
* bufferOverride
flag is enabled.
*
* 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
* bufferOverride
flag is enabled.
*
* -
*
STEREO_LEFT
specifies that rendering is done into
* the left eye.
*
* -
*
STEREO_RIGHT
specifies that rendering is done into
* the right eye.
*
* -
*
STEREO_BOTH
specifies that rendering is done into
* both eyes. This is the default.
*
*
*
*
* 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 STEREO_LEFT
,
* STEREO_RIGHT
, or STEREO_BOTH
.
*
* @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; jflush(true) 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 not 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 not 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();
}
}
}
}