aboutsummaryrefslogtreecommitdiffstats
path: root/src/classes
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2008-06-21 02:52:27 +0000
committerSven Gothel <[email protected]>2008-06-21 02:52:27 +0000
commit32f216838432d0a67ce78061aa8a09261e3c3716 (patch)
tree4407ecaa0b3e77cfac2e35978d770c8c888edf06 /src/classes
parentc055a37c8e11be9fc7bb49fcdda5b01d3fa8be43 (diff)
*** empty log message ***
git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/svn-server-sync/jogl/branches/JOGL_2_SANDBOX@1669 232f8b59-042b-4e1e-8c03-345bb8c30851
Diffstat (limited to 'src/classes')
-rwxr-xr-xsrc/classes/com/sun/opengl/util/texture/spi/DDSImage.java3
-rwxr-xr-xsrc/classes/com/sun/opengl/util/texture/spi/LEDataInputStream.java2
-rwxr-xr-xsrc/classes/com/sun/opengl/util/texture/spi/SGIImage.java7
-rwxr-xr-xsrc/classes/com/sun/opengl/util/texture/spi/TGAImage.java6
-rwxr-xr-xsrc/classes/com/sun/opengl/util/texture/spi/TextureProvider.java165
-rw-r--r--src/classes/javax/media/opengl/GLProfile.java148
-rw-r--r--src/classes/javax/media/opengl/j2d/gl2/GL2JPanel.java1319
-rwxr-xr-xsrc/classes/javax/media/opengl/util/Animator.java213
-rwxr-xr-xsrc/classes/javax/media/opengl/util/BufferUtil.java.javame_cdc_fp271
-rwxr-xr-xsrc/classes/javax/media/opengl/util/BufferUtil.java.javase288
-rwxr-xr-xsrc/classes/javax/media/opengl/util/FPSAnimator.java123
-rwxr-xr-xsrc/classes/javax/media/opengl/util/Gamma.java106
-rw-r--r--src/classes/javax/media/opengl/util/ImmModeSink.java318
-rw-r--r--src/classes/javax/media/opengl/util/VBOBufferDraw.java379
-rw-r--r--src/classes/javax/media/opengl/util/gl2es1/VBOBufferDrawGL2ES1.java51
-rwxr-xr-xsrc/classes/javax/media/opengl/util/swing/JAnimator.java207
-rwxr-xr-xsrc/classes/javax/media/opengl/util/swing/JOGLAppletLauncher.java1080
17 files changed, 4513 insertions, 173 deletions
diff --git a/src/classes/com/sun/opengl/util/texture/spi/DDSImage.java b/src/classes/com/sun/opengl/util/texture/spi/DDSImage.java
index 835025b5c..01cdf4867 100755
--- a/src/classes/com/sun/opengl/util/texture/spi/DDSImage.java
+++ b/src/classes/com/sun/opengl/util/texture/spi/DDSImage.java
@@ -44,7 +44,8 @@ import java.nio.*;
import java.nio.channels.*;
import javax.media.opengl.*;
-import com.sun.opengl.util.*;
+import javax.media.opengl.util.*;
+import com.sun.opengl.util.texture.*;
/** A reader and writer for DirectDraw Surface (.dds) files, which are
used to describe textures. These files can contain multiple mipmap
diff --git a/src/classes/com/sun/opengl/util/texture/spi/LEDataInputStream.java b/src/classes/com/sun/opengl/util/texture/spi/LEDataInputStream.java
index edbb6e35e..8baa1f414 100755
--- a/src/classes/com/sun/opengl/util/texture/spi/LEDataInputStream.java
+++ b/src/classes/com/sun/opengl/util/texture/spi/LEDataInputStream.java
@@ -68,7 +68,7 @@ import java.io.IOException;
* @author Robin Luiten
* @version 1.1 15/Dec/1997
*/
-class LEDataInputStream extends FilterInputStream implements DataInput
+public class LEDataInputStream extends FilterInputStream implements DataInput
{
/**
* To reuse some of the non endian dependent methods from
diff --git a/src/classes/com/sun/opengl/util/texture/spi/SGIImage.java b/src/classes/com/sun/opengl/util/texture/spi/SGIImage.java
index 12523eb18..5360dc01d 100755
--- a/src/classes/com/sun/opengl/util/texture/spi/SGIImage.java
+++ b/src/classes/com/sun/opengl/util/texture/spi/SGIImage.java
@@ -43,10 +43,6 @@ import java.io.*;
import javax.media.opengl.*;
import com.sun.opengl.util.*;
-// Test harness
-import java.awt.image.*;
-import javax.swing.*;
-
/** <p> Reads and writes SGI RGB/RGBA images. </p>
<p> Written from <a href =
@@ -637,6 +633,9 @@ public class SGIImage {
// Test case
/*
+ import java.awt.image.*;
+ import javax.swing.*;
+
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
try {
diff --git a/src/classes/com/sun/opengl/util/texture/spi/TGAImage.java b/src/classes/com/sun/opengl/util/texture/spi/TGAImage.java
index 8ab095643..e9e414891 100755
--- a/src/classes/com/sun/opengl/util/texture/spi/TGAImage.java
+++ b/src/classes/com/sun/opengl/util/texture/spi/TGAImage.java
@@ -44,6 +44,8 @@ import java.nio.*;
import java.nio.channels.*;
import javax.media.opengl.*;
import com.sun.opengl.util.*;
+import com.sun.opengl.util.texture.spi.*;
+import com.sun.opengl.util.texture.*;
/**
* Targa image reader and writer adapted from sources of the <a href =
@@ -294,10 +296,10 @@ public class TGAImage {
byte[] tmpData = new byte[rawWidth * header.height()];
if (header.pixelDepth() == 24) {
- format = GL.GL_BGR;
+ format = GL2.GL_BGR;
} else {
assert header.pixelDepth() == 32;
- format = GL.GL_BGRA;
+ format = GL2.GL_BGRA;
}
for (i = 0; i < header.height(); ++i) {
diff --git a/src/classes/com/sun/opengl/util/texture/spi/TextureProvider.java b/src/classes/com/sun/opengl/util/texture/spi/TextureProvider.java
deleted file mode 100755
index 1c822ef02..000000000
--- a/src/classes/com/sun/opengl/util/texture/spi/TextureProvider.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistribution of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistribution in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of Sun Microsystems, Inc. or the names of
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * This software is provided "AS IS," without a warranty of any kind. ALL
- * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
- * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
- * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
- * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
- * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
- * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
- * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
- * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
- * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
- * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
- * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
- *
- * You acknowledge that this software is not designed or intended for use
- * in the design, construction, operation or maintenance of any nuclear
- * facility.
- *
- * Sun gratefully acknowledges that this software was originally authored
- * and developed by Kenneth Bradley Russell and Christopher John Kline.
- */
-
-package com.sun.opengl.util.texture.spi;
-
-import java.io.*;
-import java.net.*;
-
-import com.sun.opengl.util.texture.*;
-
-/** Plug-in interface to TextureIO to support reading OpenGL textures
- from new file formats. For all methods, either internalFormat or
- pixelFormat may be 0 in which case they must be inferred as
- e.g. RGB or RGBA depending on the file contents.
-*/
-
-public interface TextureProvider {
-
- /**
- * Produces a TextureData object from a file, or returns null if the
- * file format was not supported by this TextureProvider. Does not
- * do any OpenGL-related work. The resulting TextureData can be
- * converted into an OpenGL texture in a later step.
- *
- * @param file the file from which to read the texture data
- *
- * @param internalFormat the OpenGL internal format to be used for
- * the texture, or 0 if it should be inferred
- * from the file's contents
- *
- * @param pixelFormat the OpenGL pixel format to be used for
- * the texture, or 0 if it should be inferred
- * from the file's contents
- *
- * @param mipmap whether mipmaps should be produced for this
- * texture either by autogenerating them or
- * reading them from the file. Some file formats
- * support multiple mipmaps in a single file in
- * which case those mipmaps will be used rather
- * than generating them.
- *
- * @param fileSuffix the file suffix to be used as a hint to the
- * provider to more quickly decide whether it
- * can handle the file, or null if the
- * provider should infer the type from the
- * file's contents
- *
- * @throws IOException if an error occurred while reading the file
- */
- public TextureData newTextureData(File file,
- int internalFormat,
- int pixelFormat,
- boolean mipmap,
- String fileSuffix) throws IOException;
-
- /**
- * Produces a TextureData object from a stream, or returns null if
- * the file format was not supported by this TextureProvider. Does
- * not do any OpenGL-related work. The resulting TextureData can be
- * converted into an OpenGL texture in a later step.
- *
- * @param stream the stream from which to read the texture data
- *
- * @param internalFormat the OpenGL internal format to be used for
- * the texture, or 0 if it should be inferred
- * from the file's contents
- *
- * @param pixelFormat the OpenGL pixel format to be used for
- * the texture, or 0 if it should be inferred
- * from the file's contents
- *
- * @param mipmap whether mipmaps should be produced for this
- * texture either by autogenerating them or
- * reading them from the file. Some file formats
- * support multiple mipmaps in a single file in
- * which case those mipmaps will be used rather
- * than generating them.
- *
- * @param fileSuffix the file suffix to be used as a hint to the
- * provider to more quickly decide whether it
- * can handle the file, or null if the
- * provider should infer the type from the
- * file's contents
- *
- * @throws IOException if an error occurred while reading the stream
- */
- public TextureData newTextureData(InputStream stream,
- int internalFormat,
- int pixelFormat,
- boolean mipmap,
- String fileSuffix) throws IOException;
-
- /**
- * Produces a TextureData object from a URL, or returns null if the
- * file format was not supported by this TextureProvider. Does not
- * do any OpenGL-related work. The resulting TextureData can be
- * converted into an OpenGL texture in a later step.
- *
- * @param url the URL from which to read the texture data
- *
- * @param internalFormat the OpenGL internal format to be used for
- * the texture, or 0 if it should be inferred
- * from the file's contents
- *
- * @param pixelFormat the OpenGL pixel format to be used for
- * the texture, or 0 if it should be inferred
- * from the file's contents
- *
- * @param mipmap whether mipmaps should be produced for this
- * texture either by autogenerating them or
- * reading them from the file. Some file formats
- * support multiple mipmaps in a single file in
- * which case those mipmaps will be used rather
- * than generating them.
- *
- * @param fileSuffix the file suffix to be used as a hint to the
- * provider to more quickly decide whether it
- * can handle the file, or null if the
- * provider should infer the type from the
- * file's contents
- *
- * @throws IOException if an error occurred while reading the URL
- */
- public TextureData newTextureData(URL url,
- int internalFormat,
- int pixelFormat,
- boolean mipmap,
- String fileSuffix) throws IOException;
-}
diff --git a/src/classes/javax/media/opengl/GLProfile.java b/src/classes/javax/media/opengl/GLProfile.java
new file mode 100644
index 000000000..245cdd95f
--- /dev/null
+++ b/src/classes/javax/media/opengl/GLProfile.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ */
+
+package javax.media.opengl;
+
+import java.lang.reflect.*;
+import java.security.*;
+import com.sun.opengl.impl.*;
+
+public class GLProfile {
+ /** The desktop (OpenGL 2.0) profile */
+ public static final String GL2 = "GL2";
+
+ /** The OpenGL ES 1 (really, 1.1) profile */
+ public static final String GLES1 = "GLES1";
+
+ /** The OpenGL ES 2 (really, 2.0) profile */
+ public static final String GLES2 = "GLES2";
+
+ /** The JVM/process wide choosen GL profile **/
+ private static String profile = null;
+
+ public static final void setProfile(String profile)
+ throws GLException
+ {
+ if(null==GLProfile.profile) {
+ // preset profile .. 1st come 1st serve
+ GLProfile.profile = profile;
+ } else {
+ if(!GLProfile.profile.equals(profile)) {
+ throw new GLException("Choosen profile ("+profile+") doesn't match preset one: "+GLProfile.profile);
+ }
+ }
+ }
+
+ public static final String getProfile() {
+ return profile;
+ }
+
+ public static final boolean isGL2() {
+ return GL2.equals(profile);
+ }
+
+ public static final boolean isGLES1() {
+ return GLES1.equals(profile);
+ }
+
+ public static final boolean isGL2ES1() {
+ return isGL2() || isGLES1();
+ }
+
+ public static final boolean isGLES2() {
+ return GLES2.equals(profile);
+ }
+
+ public static final boolean isGL2ES2() {
+ return isGL2() || isGLES2();
+ }
+
+ public static final boolean isGLES() {
+ return isGLES2() || isGLES1();
+ }
+
+ public static final boolean matches(String test_profile) {
+ return (null==test_profile)?false:test_profile.equals(profile);
+ }
+
+ public static final boolean instanceOf(Object obj, String clazzName) {
+ Class clazz = obj.getClass();
+ do {
+ if(clazz.getName().equals(clazzName)) {
+ return true;
+ }
+ clazz = clazz.getSuperclass();
+ } while (clazz!=null);
+ return false;
+ }
+
+ public static final boolean implementationOf(Object obj, String faceName) {
+ Class[] clazzes = obj.getClass().getInterfaces();
+ for(int i=clazzes.length-1; i>=0; i--) {
+ Class face = clazzes[i];
+ if(face.getName().equals(faceName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static final boolean implementationOfGL2(Object obj) {
+ return implementationOf(obj, "javax.media.opengl.GL2");
+ }
+
+ public static final boolean implementationOfGLES1(Object obj) {
+ return implementationOf(obj, "javax.media.opengl.GLES1");
+ }
+
+ public static final boolean implementationOfGLES2(Object obj) {
+ return implementationOf(obj, "javax.media.opengl.GLES2");
+ }
+
+ public static final boolean implementationOfGLES(Object obj) {
+ return implementationOfGLES1(obj) || implementationOfGLES2(obj);
+ }
+
+ public static final boolean implementationOfGL2ES1(Object obj) {
+ return implementationOf(obj, "javax.media.opengl.GL2ES1");
+ }
+
+ public static final boolean implementationOfGL2ES2(Object obj) {
+ return implementationOf(obj, "javax.media.opengl.GL2ES2");
+ }
+
+}
+
diff --git a/src/classes/javax/media/opengl/j2d/gl2/GL2JPanel.java b/src/classes/javax/media/opengl/j2d/gl2/GL2JPanel.java
new file mode 100644
index 000000000..ca21c7777
--- /dev/null
+++ b/src/classes/javax/media/opengl/j2d/gl2/GL2JPanel.java
@@ -0,0 +1,1319 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ *
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package javax.media.opengl.j2d.gl2;
+
+import javax.media.opengl.*;
+import javax.media.opengl.awt.*;
+
+import java.awt.*;
+import java.awt.geom.*;
+import java.awt.image.*;
+import java.beans.*;
+import javax.swing.*;
+import java.nio.*;
+import java.security.*;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import com.sun.opengl.impl.*;
+import com.sun.opengl.impl.awt.*;
+import com.sun.opengl.impl.j2d.*;
+
+// FIXME: Subclasses need to call resetGLFunctionAvailability() on their
+// context whenever the displayChanged() function is called on their
+// GLEventListeners
+
+/** A lightweight Swing component which provides OpenGL rendering
+ support. Provided for compatibility with Swing user interfaces
+ when adding a heavyweight doesn't work either because of
+ Z-ordering or LayoutManager problems. <P>
+
+ The GL2JPanel can be made transparent by creating it with a
+ GLCapabilities object with alpha bits specified and calling {@link
+ #setOpaque}(false). Pixels with resulting OpenGL alpha values less
+ than 1.0 will be overlaid on any underlying Swing rendering. <P>
+
+ Notes specific to the Reference Implementation: This component
+ attempts to use hardware-accelerated rendering via pbuffers and
+ falls back on to software rendering if problems occur.
+ Note that because this component attempts to use pbuffers for
+ rendering, and because pbuffers can not be resized, somewhat
+ surprising behavior may occur during resize operations; the {@link
+ GLEventListener#init} method may be called multiple times as the
+ pbuffer is resized to be able to cover the size of the GL2JPanel.
+ This behavior is correct, as the textures and display lists for
+ the GL2JPanel will have been lost during the resize operation. The
+ application should attempt to make its GLEventListener.init()
+ methods as side-effect-free as possible. <P>
+
+*/
+
+public class GL2JPanel extends JPanel implements AWTGLAutoDrawable {
+ private static final boolean DEBUG = Debug.debug("GL2JPanel");
+ private static final boolean VERBOSE = Debug.verbose();
+
+ private GLDrawableHelper drawableHelper = new GLDrawableHelper();
+ private volatile boolean isInitialized;
+ private volatile boolean shouldInitialize = false;
+
+ // Data used for either pbuffers or pixmap-based offscreen surfaces
+ private GLCapabilities offscreenCaps;
+ private GLCapabilitiesChooser chooser;
+ private GLContext shareWith;
+ // This image is exactly the correct size to render into the panel
+ private BufferedImage offscreenImage;
+ // One of these is used to store the read back pixels before storing
+ // in the BufferedImage
+ private ByteBuffer readBackBytes;
+ private IntBuffer readBackInts;
+ private int readBackWidthInPixels;
+ private int readBackHeightInPixels;
+ // Width of the actual GL2JPanel
+ private int panelWidth = 0;
+ private int panelHeight = 0;
+ private Updater updater;
+ private int awtFormat;
+ private int glFormat;
+ private int glType;
+ // Lazy reshape notification
+ private boolean handleReshape = false;
+ private boolean sendReshape = true;
+
+ private static GLDrawableFactoryImpl factory;
+
+ // Implementation using pbuffers
+ private static boolean hardwareAccelerationDisabled =
+ Debug.isPropertyDefined("jogl.gljpanel.nohw");
+ private static boolean softwareRenderingDisabled =
+ Debug.isPropertyDefined("jogl.gljpanel.nosw");
+ private GLPbuffer pbuffer;
+ private int pbufferWidth = 256;
+ private int pbufferHeight = 256;
+
+ // Implementation using software rendering
+ private GLDrawableImpl offscreenDrawable;
+ private GLContextImpl offscreenContext;
+
+ // For handling reshape events lazily
+ private int reshapeX;
+ private int reshapeY;
+ private int reshapeWidth;
+ private int reshapeHeight;
+
+ // For saving/restoring of OpenGL state during ReadPixels
+ private int[] swapbytes = new int[1];
+ private int[] rowlength = new int[1];
+ private int[] skiprows = new int[1];
+ private int[] skippixels = new int[1];
+ private int[] alignment = new int[1];
+
+ // Implementation using Java2D OpenGL pipeline's back buffer
+ private boolean oglPipelineEnabled =
+ Java2D.isOGLPipelineActive() &&
+ !Debug.isPropertyDefined("jogl.gljpanel.noogl");
+ // Opaque Object identifier representing the Java2D surface we are
+ // drawing to; used to determine when to destroy and recreate JOGL
+ // context
+ private Object j2dSurface;
+ // Graphics object being used during Java2D update action
+ // (absolutely essential to cache this)
+ private Graphics cached2DGraphics;
+ // No-op context representing the Java2D OpenGL context
+ private GLContext j2dContext;
+ // Context associated with no-op drawable representing the JOGL
+ // OpenGL context
+ private GLDrawable joglDrawable;
+ // The real OpenGL context JOGL uses to render
+ private GLContext joglContext;
+ // State captured from Java2D OpenGL context necessary in order to
+ // properly render into Java2D back buffer
+ private int[] drawBuffer = new int[1];
+ private int[] readBuffer = new int[1];
+ // This is required when the FBO option of the Java2D / OpenGL
+ // pipeline is active
+ private int[] frameBuffer = new int[1];
+ // Current (as of this writing) NVidia drivers have a couple of bugs
+ // relating to the sharing of framebuffer and renderbuffer objects
+ // between contexts. It appears we have to (a) reattach the color
+ // attachment and (b) actually create new depth buffer storage and
+ // attach it in order for the FBO to behave properly in our context.
+ private boolean checkedForFBObjectWorkarounds;
+ private boolean fbObjectWorkarounds;
+ private int[] frameBufferDepthBuffer;
+ private int[] frameBufferTexture;
+ private boolean createNewDepthBuffer;
+ // Current (as of this writing) ATI drivers have problems when the
+ // same FBO is bound in two different contexts. Here we check for
+ // this case and explicitly release the FBO from Java2D's context
+ // before switching to ours. Java2D will re-bind the FBO when it
+ // makes its context current the next time. Interestingly, if we run
+ // this code path on NVidia hardware, it breaks the rendering
+ // results -- no output is generated. This doesn't appear to be an
+ // interaction with the abovementioned NVidia-specific workarounds,
+ // as even if we disable that code the FBO is still reported as
+ // incomplete in our context.
+ private boolean checkedGLVendor;
+ private boolean vendorIsATI;
+
+ // Holding on to this GraphicsConfiguration is a workaround for a
+ // problem in the Java 2D / JOGL bridge when FBOs are enabled; see
+ // comment related to Issue 274 below
+ private GraphicsConfiguration workaroundConfig;
+
+ // These are always set to (0, 0) except when the Java2D / OpenGL
+ // pipeline is active
+ private int viewportX;
+ private int viewportY;
+
+ static {
+ // Force eager initialization of part of the Java2D class since
+ // otherwise it's likely it will try to be initialized while on
+ // the Queue Flusher Thread, which is not allowed
+ if (Java2D.isOGLPipelineActive() && Java2D.isFBOEnabled()) {
+ Java2D.getShareContext(GraphicsEnvironment.
+ getLocalGraphicsEnvironment().
+ getDefaultScreenDevice().
+ getDefaultConfiguration());
+ }
+ GLProfile.setProfile(GLProfile.GL2);
+ factory = GLDrawableFactoryImpl.getFactoryImpl(true);
+ }
+
+ /** Creates a new GL2JPanel component with a default set of OpenGL
+ capabilities and using the default OpenGL capabilities selection
+ mechanism. */
+ public GL2JPanel() {
+ this(null);
+ }
+
+ /** Creates a new GL2JPanel component with the requested set of
+ OpenGL capabilities, using the default OpenGL capabilities
+ selection mechanism. */
+ public GL2JPanel(GLCapabilities capabilities) {
+ this(capabilities, null, null);
+ }
+
+ /** Creates a new GL2JPanel component. The passed GLCapabilities
+ specifies the OpenGL capabilities for the component; if null, a
+ default set of capabilities is used. The GLCapabilitiesChooser
+ specifies the algorithm for selecting one of the available
+ GLCapabilities for the component; a DefaultGLCapabilitesChooser
+ is used if null is passed for this argument. The passed
+ GLContext specifies an OpenGL context with which to share
+ textures, display lists and other OpenGL state, and may be null
+ if sharing is not desired. See the note in the overview documentation on
+ <a href="../../../overview-summary.html#SHARING">context sharing</a>.
+ */
+ public GL2JPanel(GLCapabilities capabilities, GLCapabilitiesChooser chooser, GLContext shareWith) {
+ super();
+
+ // Works around problems on many vendors' cards; we don't need a
+ // back buffer for the offscreen surface anyway
+ if (capabilities != null) {
+ offscreenCaps = (GLCapabilities) capabilities.clone();
+ } else {
+ offscreenCaps = new GLCapabilities();
+ }
+ offscreenCaps.setDoubleBuffered(false);
+ this.chooser = ((chooser != null) ? chooser : new DefaultGLCapabilitiesChooser());
+ this.shareWith = shareWith;
+ }
+
+ public void display() {
+ if (EventQueue.isDispatchThread()) {
+ // Want display() to be synchronous, so call paintImmediately()
+ paintImmediately(0, 0, getWidth(), getHeight());
+ } else {
+ // Multithreaded redrawing of Swing components is not allowed,
+ // so do everything on the event dispatch thread
+ try {
+ EventQueue.invokeAndWait(paintImmediatelyAction);
+ } catch (Exception e) {
+ throw new GLException(e);
+ }
+ }
+ }
+
+ private void captureJ2DState(GL2 gl, Graphics g) {
+ gl.glGetIntegerv(GL2.GL_DRAW_BUFFER, drawBuffer, 0);
+ gl.glGetIntegerv(GL2.GL_READ_BUFFER, readBuffer, 0);
+ if (Java2D.isFBOEnabled() &&
+ Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT) {
+ if (DEBUG && VERBOSE) {
+ System.err.println("GL2JPanel: Fetching GL_FRAMEBUFFER_BINDING_EXT");
+ }
+ gl.glGetIntegerv(GL2.GL_FRAMEBUFFER_BINDING_EXT, frameBuffer, 0);
+
+ if (fbObjectWorkarounds ||
+ !checkedForFBObjectWorkarounds) {
+ // See above for description of what we are doing here
+ if (frameBufferTexture == null)
+ frameBufferTexture = new int[1];
+
+ // Query the framebuffer for its color buffer so we can hook
+ // it back up in our context (should not be necessary)
+ gl.glGetFramebufferAttachmentParameteriv(GL2.GL_FRAMEBUFFER_EXT,
+ GL2.GL_COLOR_ATTACHMENT0_EXT,
+ GL2.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT,
+ frameBufferTexture, 0);
+ if (DEBUG && VERBOSE) {
+ System.err.println("GL2JPanel: FBO COLOR_ATTACHMENT0: " + frameBufferTexture[0]);
+ }
+ }
+
+ if (!checkedGLVendor) {
+ checkedGLVendor = true;
+ String vendor = gl.glGetString(GL2.GL_VENDOR);
+
+ if ((vendor != null) &&
+ vendor.startsWith("ATI")) {
+ vendorIsATI = true;
+ }
+ }
+
+ if (vendorIsATI) {
+ // Unbind the FBO from Java2D's context as it appears that
+ // driver bugs on ATI's side are causing problems if the FBO is
+ // simultaneously bound to more than one context. Java2D will
+ // re-bind the FBO during the next validation of its context.
+ // Note: this breaks rendering at least on NVidia hardware
+ gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER_EXT, 0);
+ }
+ }
+ }
+
+ private boolean preGL(Graphics g) {
+ GL2 gl = joglContext.getGL().getGL2();
+ // Set up needed state in JOGL context from Java2D context
+ gl.glEnable(GL2.GL_SCISSOR_TEST);
+ Rectangle r = Java2D.getOGLScissorBox(g);
+
+ if (r == null) {
+ if (DEBUG && VERBOSE) {
+ System.err.println("Java2D.getOGLScissorBox() returned null");
+ }
+ return false;
+ }
+ if (DEBUG && VERBOSE) {
+ System.err.println("GL2JPanel: gl.glScissor(" + r.x + ", " + r.y + ", " + r.width + ", " + r.height + ")");
+ }
+
+ gl.glScissor(r.x, r.y, r.width, r.height);
+ Rectangle oglViewport = Java2D.getOGLViewport(g, panelWidth, panelHeight);
+ // If the viewport X or Y changes, in addition to the panel's
+ // width or height, we need to send a reshape operation to the
+ // client
+ if ((viewportX != oglViewport.x) ||
+ (viewportY != oglViewport.y)) {
+ sendReshape = true;
+ if (DEBUG) {
+ System.err.println("Sending reshape because viewport changed");
+ System.err.println(" viewportX (" + viewportX + ") ?= oglViewport.x (" + oglViewport.x + ")");
+ System.err.println(" viewportY (" + viewportY + ") ?= oglViewport.y (" + oglViewport.y + ")");
+ }
+ }
+ viewportX = oglViewport.x;
+ viewportY = oglViewport.y;
+
+ // If the FBO option is active, bind to the FBO from the Java2D
+ // context.
+ // Note that all of the plumbing in the context sharing stuff will
+ // allow us to bind to this object since it's in our namespace.
+ if (Java2D.isFBOEnabled() &&
+ Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT) {
+ if (DEBUG && VERBOSE) {
+ System.err.println("GL2JPanel: Binding to framebuffer object " + frameBuffer[0]);
+ }
+
+ // The texture target for Java2D's OpenGL pipeline when using FBOs
+ // -- either GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE_ARB
+ int fboTextureTarget = Java2D.getOGLTextureType(g);
+
+ if (!checkedForFBObjectWorkarounds) {
+ checkedForFBObjectWorkarounds = true;
+ gl.glBindTexture(fboTextureTarget, 0);
+ gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER_EXT, frameBuffer[0]);
+ if (gl.glCheckFramebufferStatus(GL2.GL_FRAMEBUFFER_EXT) !=
+ GL2.GL_FRAMEBUFFER_COMPLETE_EXT) {
+ // Need to do workarounds
+ fbObjectWorkarounds = true;
+ createNewDepthBuffer = true;
+ if (DEBUG) {
+ System.err.println("-- GL2JPanel: discovered frame_buffer_object workarounds to be necessary");
+ }
+ } else {
+ // Don't need the frameBufferTexture temporary any more
+ frameBufferTexture = null;
+ }
+ }
+
+ if (fbObjectWorkarounds && createNewDepthBuffer) {
+ if (frameBufferDepthBuffer == null)
+ frameBufferDepthBuffer = new int[1];
+
+ // Create our own depth renderbuffer and associated storage
+ // If we have an old one, delete it
+ if (frameBufferDepthBuffer[0] != 0) {
+ gl.glDeleteRenderbuffers(1, frameBufferDepthBuffer, 0);
+ frameBufferDepthBuffer[0] = 0;
+ }
+
+ gl.glBindTexture(fboTextureTarget, frameBufferTexture[0]);
+ int[] width = new int[1];
+ int[] height = new int[1];
+ gl.glGetTexLevelParameteriv(fboTextureTarget, 0, GL2.GL_TEXTURE_WIDTH, width, 0);
+ gl.glGetTexLevelParameteriv(fboTextureTarget, 0, GL2.GL_TEXTURE_HEIGHT, height, 0);
+
+ gl.glGenRenderbuffers(1, frameBufferDepthBuffer, 0);
+ if (DEBUG) {
+ System.err.println("GL2JPanel: Generated frameBufferDepthBuffer " + frameBufferDepthBuffer[0] +
+ " with width " + width[0] + ", height " + height[0]);
+ }
+
+ gl.glBindRenderbuffer(GL2.GL_RENDERBUFFER_EXT, frameBufferDepthBuffer[0]);
+ // FIXME: may need a loop here like in Java2D
+ gl.glRenderbufferStorage(GL2.GL_RENDERBUFFER_EXT, GL2.GL_DEPTH_COMPONENT24, width[0], height[0]);
+
+ gl.glBindRenderbuffer(GL2.GL_RENDERBUFFER_EXT, 0);
+ createNewDepthBuffer = false;
+ }
+
+ gl.glBindTexture(fboTextureTarget, 0);
+ gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER_EXT, frameBuffer[0]);
+
+ if (fbObjectWorkarounds) {
+ // Hook up the color and depth buffer attachment points for this framebuffer
+ gl.glFramebufferTexture2D(GL2.GL_FRAMEBUFFER_EXT,
+ GL2.GL_COLOR_ATTACHMENT0_EXT,
+ fboTextureTarget,
+ frameBufferTexture[0],
+ 0);
+ if (DEBUG && VERBOSE) {
+ System.err.println("GL2JPanel: frameBufferDepthBuffer: " + frameBufferDepthBuffer[0]);
+ }
+ gl.glFramebufferRenderbuffer(GL2.GL_FRAMEBUFFER_EXT,
+ GL2.GL_DEPTH_ATTACHMENT_EXT,
+ GL2.GL_RENDERBUFFER_EXT,
+ frameBufferDepthBuffer[0]);
+ }
+
+ if (DEBUG) {
+ int status = gl.glCheckFramebufferStatus(GL2.GL_FRAMEBUFFER_EXT);
+ if (status != GL2.GL_FRAMEBUFFER_COMPLETE_EXT) {
+ throw new GLException("Error: framebuffer was incomplete: status = 0x" +
+ Integer.toHexString(status));
+ }
+ }
+ } else {
+ if (DEBUG && VERBOSE) {
+ System.err.println("GL2JPanel: Setting up drawBuffer " + drawBuffer[0] +
+ " and readBuffer " + readBuffer[0]);
+ }
+
+ gl.glDrawBuffer(drawBuffer[0]);
+ gl.glReadBuffer(readBuffer[0]);
+ }
+
+ return true;
+ }
+
+ private void postGL(Graphics g) {
+ if (Java2D.isFBOEnabled() &&
+ Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT) {
+ // Unbind the framebuffer from our context to work around
+ // apparent driver bugs or at least unspecified behavior causing
+ // OpenGL to run out of memory with certain cards and drivers
+ GL2 gl = joglContext.getGL().getGL2();
+ gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER_EXT, 0);
+ }
+ }
+
+ /** Overridden to cause OpenGL rendering to be performed during
+ repaint cycles. Subclasses which override this method must call
+ super.paintComponent() in their paintComponent() method in order
+ to function properly. <P>
+
+ <B>Overrides:</B>
+ <DL><DD><CODE>paintComponent</CODE> in class <CODE>javax.swing.JComponent</CODE></DD></DL> */
+ protected void paintComponent(final Graphics g) {
+ if (Beans.isDesignTime()) {
+ // Make GL2JPanel behave better in NetBeans GUI builder
+ g.setColor(Color.BLACK);
+ g.fillRect(0, 0, getWidth(), getHeight());
+ FontMetrics fm = g.getFontMetrics();
+ String name = getName();
+ if (name == null) {
+ name = getClass().getName();
+ int idx = name.lastIndexOf('.');
+ if (idx >= 0) {
+ name = name.substring(idx + 1);
+ }
+ }
+ Rectangle2D bounds = fm.getStringBounds(name, g);
+ g.setColor(Color.WHITE);
+ g.drawString(name,
+ (int) ((getWidth() - bounds.getWidth()) / 2),
+ (int) ((getHeight() + bounds.getHeight()) / 2));
+ return;
+ }
+
+ if (shouldInitialize) {
+ initialize();
+ }
+
+ if (!isInitialized) {
+ return;
+ }
+
+ // NOTE: must do this when the context is not current as it may
+ // involve destroying the pbuffer (current context) and
+ // re-creating it -- tricky to do properly while the context is
+ // current
+ if (handleReshape) {
+ handleReshape();
+ }
+
+ updater.setGraphics(g);
+
+ if (oglPipelineEnabled) {
+
+ // This is a workaround for an issue in the Java 2D / JOGL
+ // bridge (reported by an end user as JOGL Issue 274) where Java
+ // 2D can occasionally leave its internal OpenGL context current
+ // to the on-screen window rather than its internal "scratch"
+ // pbuffer surface to which the FBO is attached. JOGL expects to
+ // find a stable OpenGL drawable (on Windows, an HDC) upon which
+ // it can create another OpenGL context. It turns out that, on
+ // Windows, when Java 2D makes its internal OpenGL context
+ // current against the window in order to put pixels on the
+ // screen, it gets the device context for the window, makes its
+ // context current, and releases the device context. This means
+ // that when JOGL's Runnable gets to run below, the HDC is
+ // already invalid. The workaround for this is to force Java 2D
+ // to make its context current to the scratch surface, which we
+ // can do by executing an empty Runnable with the "shared"
+ // context current. This will be fixed in a Java SE 6 update
+ // release, hopefully 6u2.
+ if (Java2D.isFBOEnabled()) {
+ if (workaroundConfig == null) {
+ workaroundConfig = GraphicsEnvironment.
+ getLocalGraphicsEnvironment().
+ getDefaultScreenDevice().
+ getDefaultConfiguration();
+ }
+ Java2D.invokeWithOGLSharedContextCurrent(workaroundConfig, new Runnable() { public void run() {}});
+ }
+
+ Java2D.invokeWithOGLContextCurrent(g, new Runnable() {
+ public void run() {
+ if (DEBUG && VERBOSE) {
+ System.err.println("-- In invokeWithOGLContextCurrent");
+ }
+
+ // Create no-op context representing Java2D context
+ if (j2dContext == null) {
+ j2dContext = factory.createExternalGLContext();
+ if (DEBUG) {
+ j2dContext.setGL(new DebugGL2(j2dContext.getGL().getGL2()));
+ }
+
+ // Check to see whether we can support the requested
+ // capabilities or need to fall back to a pbuffer
+ // FIXME: add more checks?
+
+ j2dContext.makeCurrent();
+ GL2 gl = j2dContext.getGL().getGL2();
+ if ((getGLInteger(gl, GL2.GL_RED_BITS) < offscreenCaps.getRedBits()) ||
+ (getGLInteger(gl, GL2.GL_GREEN_BITS) < offscreenCaps.getGreenBits()) ||
+ (getGLInteger(gl, GL2.GL_BLUE_BITS) < offscreenCaps.getBlueBits()) ||
+ // (getGLInteger(gl, GL2.GL_ALPHA_BITS) < offscreenCaps.getAlphaBits()) ||
+ (getGLInteger(gl, GL2.GL_ACCUM_RED_BITS) < offscreenCaps.getAccumRedBits()) ||
+ (getGLInteger(gl, GL2.GL_ACCUM_GREEN_BITS) < offscreenCaps.getAccumGreenBits()) ||
+ (getGLInteger(gl, GL2.GL_ACCUM_BLUE_BITS) < offscreenCaps.getAccumBlueBits()) ||
+ (getGLInteger(gl, GL2.GL_ACCUM_ALPHA_BITS) < offscreenCaps.getAccumAlphaBits()) ||
+ // (getGLInteger(gl, GL2.GL_DEPTH_BITS) < offscreenCaps.getDepthBits()) ||
+ (getGLInteger(gl, GL2.GL_STENCIL_BITS) < offscreenCaps.getStencilBits())) {
+ if (DEBUG) {
+ System.err.println("GL2JPanel: Falling back to pbuffer-based support because Java2D context insufficient");
+ System.err.println(" Available Required");
+ System.err.println("GL_RED_BITS " + getGLInteger(gl, GL2.GL_RED_BITS) + " " + offscreenCaps.getRedBits());
+ System.err.println("GL_GREEN_BITS " + getGLInteger(gl, GL2.GL_GREEN_BITS) + " " + offscreenCaps.getGreenBits());
+ System.err.println("GL_BLUE_BITS " + getGLInteger(gl, GL2.GL_BLUE_BITS) + " " + offscreenCaps.getBlueBits());
+ System.err.println("GL_ALPHA_BITS " + getGLInteger(gl, GL2.GL_ALPHA_BITS) + " " + offscreenCaps.getAlphaBits());
+ System.err.println("GL_ACCUM_RED_BITS " + getGLInteger(gl, GL2.GL_ACCUM_RED_BITS) + " " + offscreenCaps.getAccumRedBits());
+ System.err.println("GL_ACCUM_GREEN_BITS " + getGLInteger(gl, GL2.GL_ACCUM_GREEN_BITS) + " " + offscreenCaps.getAccumGreenBits());
+ System.err.println("GL_ACCUM_BLUE_BITS " + getGLInteger(gl, GL2.GL_ACCUM_BLUE_BITS) + " " + offscreenCaps.getAccumBlueBits());
+ System.err.println("GL_ACCUM_ALPHA_BITS " + getGLInteger(gl, GL2.GL_ACCUM_ALPHA_BITS) + " " + offscreenCaps.getAccumAlphaBits());
+ System.err.println("GL_DEPTH_BITS " + getGLInteger(gl, GL2.GL_DEPTH_BITS) + " " + offscreenCaps.getDepthBits());
+ System.err.println("GL_STENCIL_BITS " + getGLInteger(gl, GL2.GL_STENCIL_BITS) + " " + offscreenCaps.getStencilBits());
+ }
+ isInitialized = false;
+ shouldInitialize = true;
+ oglPipelineEnabled = false;
+ handleReshape = true;
+ j2dContext.release();
+ j2dContext.destroy();
+ j2dContext = null;
+ return;
+ }
+ j2dContext.release();
+ }
+
+ j2dContext.makeCurrent();
+ try {
+ captureJ2DState(j2dContext.getGL().getGL2(), g);
+ Object curSurface = Java2D.getOGLSurfaceIdentifier(g);
+ if (curSurface != null) {
+ if (j2dSurface != curSurface) {
+ if (joglContext != null) {
+ joglContext.destroy();
+ joglContext = null;
+ joglDrawable = null;
+ sendReshape = true;
+ if (DEBUG) {
+ System.err.println("Sending reshape because surface changed");
+ System.err.println("New surface = " + curSurface);
+ }
+ }
+ j2dSurface = curSurface;
+ }
+ if (joglContext == null) {
+ if (factory.canCreateExternalGLDrawable()) {
+ joglDrawable = factory.createExternalGLDrawable();
+ joglContext = joglDrawable.createContext(shareWith);
+ } else if (factory.canCreateContextOnJava2DSurface()) {
+ // Mac OS X code path
+ joglContext = factory.createContextOnJava2DSurface(g, shareWith);
+ }
+ if (DEBUG) {
+ joglContext.setGL(new DebugGL2(joglContext.getGL().getGL2()));
+ }
+
+ if (Java2D.isFBOEnabled() &&
+ Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT &&
+ fbObjectWorkarounds) {
+ createNewDepthBuffer = true;
+ }
+ }
+ if (joglContext instanceof Java2DGLContext) {
+ // Mac OS X code path
+ ((Java2DGLContext) joglContext).setGraphics(g);
+ }
+
+ if (DEBUG && VERBOSE && Java2D.isFBOEnabled()) {
+ System.err.print("-- Surface type: ");
+ int surfaceType = Java2D.getOGLSurfaceType(g);
+ if (surfaceType == Java2D.UNDEFINED) {
+ System.err.println("UNDEFINED");
+ } else if (surfaceType == Java2D.WINDOW) {
+ System.err.println("WINDOW");
+ } else if (surfaceType == Java2D.PBUFFER) {
+ System.err.println("PBUFFER");
+ } else if (surfaceType == Java2D.TEXTURE) {
+ System.err.println("TEXTURE");
+ } else if (surfaceType == Java2D.FLIP_BACKBUFFER) {
+ System.err.println("FLIP_BACKBUFFER");
+ } else if (surfaceType == Java2D.FBOBJECT) {
+ System.err.println("FBOBJECT");
+ } else {
+ System.err.println("(Unknown surface type " + surfaceType + ")");
+ }
+ }
+
+ drawableHelper.invokeGL(joglDrawable, joglContext, displayAction, initAction);
+ }
+ } finally {
+ j2dContext.release();
+ }
+ }
+ });
+ } else {
+ if (!hardwareAccelerationDisabled) {
+ pbuffer.display();
+ } else {
+ drawableHelper.invokeGL(offscreenDrawable, offscreenContext, displayAction, initAction);
+ }
+
+ if (offscreenImage != null) {
+ // Draw resulting image in one shot
+ g.drawImage(offscreenImage, 0, 0, offscreenImage.getWidth(), offscreenImage.getHeight(), this);
+ }
+ }
+ }
+
+ /** Overridden to track when this component is added to a container.
+ Subclasses which override this method must call
+ super.addNotify() in their addNotify() method in order to
+ function properly. <P>
+
+ <B>Overrides:</B>
+ <DL><DD><CODE>addNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
+ public void addNotify() {
+ super.addNotify();
+ shouldInitialize = true;
+ if (DEBUG) {
+ System.err.println("GL2JPanel.addNotify()");
+ }
+ }
+
+ /** Overridden to track when this component is removed from a
+ container. Subclasses which override this method must call
+ super.removeNotify() in their removeNotify() method in order to
+ function properly. <P>
+
+ <B>Overrides:</B>
+ <DL><DD><CODE>removeNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
+ public void removeNotify() {
+ if (DEBUG) {
+ System.err.println("GL2JPanel.removeNotify()");
+ }
+ if (oglPipelineEnabled) {
+ Java2D.invokeWithOGLContextCurrent(null, new Runnable() {
+ public void run() {
+ if (joglContext != null) {
+ joglContext.destroy();
+ joglContext = null;
+ }
+ joglDrawable = null;
+ if (j2dContext != null) {
+ j2dContext.destroy();
+ j2dContext = null;
+ }
+ }
+ });
+ } else {
+ if (!hardwareAccelerationDisabled) {
+ if (pbuffer != null) {
+ pbuffer.destroy();
+ pbuffer = null;
+ }
+ } else {
+ if (offscreenContext != null) {
+ offscreenContext.destroy();
+ offscreenContext = null;
+ }
+ if (offscreenDrawable != null) {
+ offscreenDrawable.destroy();
+ offscreenDrawable = null;
+ }
+ }
+ }
+ isInitialized = false;
+ super.removeNotify();
+ }
+
+ /** Overridden to cause {@link GLDrawableHelper#reshape} to be
+ called on all registered {@link GLEventListener}s. Subclasses
+ which override this method must call super.reshape() in
+ their reshape() method in order to function properly. <P>
+
+ <B>Overrides:</B>
+ <DL><DD><CODE>reshape</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
+ public void reshape(int x, int y, int width, int height) {
+ super.reshape(x, y, width, height);
+
+ reshapeX = x;
+ reshapeY = y;
+ reshapeWidth = width;
+ reshapeHeight = height;
+ handleReshape = true;
+ }
+
+ public void setOpaque(boolean opaque) {
+ if (opaque != isOpaque()) {
+ if (offscreenImage != null) {
+ offscreenImage.flush();
+ offscreenImage = null;
+ }
+ }
+ super.setOpaque(opaque);
+ }
+
+ public void addGLEventListener(GLEventListener listener) {
+ drawableHelper.addGLEventListener(listener);
+ }
+
+ public void removeGLEventListener(GLEventListener listener) {
+ drawableHelper.removeGLEventListener(listener);
+ }
+
+ public GLContext createContext(GLContext shareWith) {
+ if (!hardwareAccelerationDisabled) {
+ return pbuffer.createContext(shareWith);
+ } else {
+ return offscreenDrawable.createContext(shareWith);
+ }
+ }
+
+ public void setRealized(boolean realized) {
+ }
+
+ public GLContext getContext() {
+ if (oglPipelineEnabled) {
+ return joglContext;
+ } else {
+ if (!hardwareAccelerationDisabled) {
+ // Workaround for crashes in NetBeans GUI builder
+ if (pbuffer == null && Beans.isDesignTime()) {
+ return null;
+ }
+ return pbuffer.getContext();
+ } else {
+ return offscreenContext;
+ }
+ }
+ }
+
+ public GL getGL() {
+ GLContext context = getContext();
+ return (context == null) ? null : context.getGL();
+ }
+
+ public void setGL(GL gl) {
+ if(!gl.isGL2()) {
+ throw new GLException("not a GL2 implementation");
+ }
+ GLContext context = getContext();
+ if (context != null) {
+ context.setGL(gl);
+ }
+ }
+
+ public void setAutoSwapBufferMode(boolean onOrOff) {
+ if (!hardwareAccelerationDisabled) {
+ // Workaround for crashes in NetBeans GUI builder
+ if (pbuffer == null && Beans.isDesignTime()) {
+ return;
+ }
+ pbuffer.setAutoSwapBufferMode(onOrOff);
+ } else {
+ drawableHelper.setAutoSwapBufferMode(onOrOff);
+ }
+ }
+
+ public boolean getAutoSwapBufferMode() {
+ if (!hardwareAccelerationDisabled && !oglPipelineEnabled) {
+ return pbuffer.getAutoSwapBufferMode();
+ } else {
+ return drawableHelper.getAutoSwapBufferMode();
+ }
+ }
+
+ public void swapBuffers() {
+ // In the current implementation this is basically a no-op. Both
+ // the pbuffer and pixmap based rendering paths use a single-
+ // buffered surface so swapping the buffers doesn't do anything.
+ // We also don't currently have the provision to skip copying the
+ // data to the Swing portion of the GL2JPanel in any of the
+ // rendering paths.
+ if (oglPipelineEnabled) {
+ // Do nothing
+ } else if (!hardwareAccelerationDisabled) {
+ pbuffer.swapBuffers();
+ } else {
+ drawableHelper.invokeGL(offscreenDrawable, offscreenContext, swapBuffersAction, initAction);
+ }
+ }
+
+ /** For a translucent GL2JPanel (one for which {@link #setOpaque
+ setOpaque}(false) has been called), indicates whether the
+ application should preserve the OpenGL color buffer
+ (GL_COLOR_BUFFER_BIT) for correct rendering of the GL2JPanel and
+ underlying widgets which may show through portions of the
+ GL2JPanel with alpha values less than 1. Most Swing
+ implementations currently expect the GL2JPanel to be completely
+ cleared (e.g., by <code>glClear(GL_COLOR_BUFFER_BIT |
+ GL_DEPTH_BUFFER_BIT)</code>), but for certain optimized Swing
+ implementations which use OpenGL internally, it may be possible
+ to perform OpenGL rendering using the GL2JPanel into the same
+ OpenGL drawable as the Swing implementation uses. */
+ public boolean shouldPreserveColorBufferIfTranslucent() {
+ return oglPipelineEnabled;
+ }
+
+ protected GLCapabilities caps=null;
+
+ public GLCapabilities getChosenGLCapabilities() {
+ if (oglPipelineEnabled) {
+ if(caps==null) {
+ caps = new GLCapabilities();
+ }
+ return caps;
+ }
+
+ if (hardwareAccelerationDisabled) {
+ if (offscreenDrawable != null)
+ return offscreenDrawable.getChosenGLCapabilities();
+ } else {
+ if (pbuffer != null)
+ return pbuffer.getChosenGLCapabilities();
+ }
+
+ return null;
+ }
+
+ public void setChosenGLCapabilities(GLCapabilities caps) {
+ if (oglPipelineEnabled) {
+ this.caps = (caps==null) ? null : (GLCapabilities) caps.clone();
+ }
+
+ if (hardwareAccelerationDisabled) {
+ if (offscreenDrawable != null)
+ offscreenDrawable.setChosenGLCapabilities(caps);
+ } else {
+ if (pbuffer != null)
+ pbuffer.setChosenGLCapabilities(caps);
+ }
+ }
+
+ public NativeWindow getNativeWindow() {
+ throw new GLException("FIXME");
+ }
+
+ public GLDrawableFactory getFactory() {
+ return factory;
+ }
+
+ public int lockSurface() throws GLException {
+ throw new GLException("FIXME");
+ }
+ public void unlockSurface() {
+ throw new GLException("FIXME");
+ }
+ public boolean isSurfaceLocked() {
+ throw new GLException("FIXME");
+ }
+ public void destroy() {
+ throw new GLException("FIXME");
+ }
+
+ //----------------------------------------------------------------------
+ // Internals only below this point
+ //
+
+ private void initialize() {
+ if (panelWidth == 0 ||
+ panelHeight == 0) {
+ // See whether we have a non-zero size yet and can go ahead with
+ // initialization
+ if (reshapeWidth == 0 ||
+ reshapeHeight == 0) {
+ return;
+ }
+
+ // Pull down reshapeWidth and reshapeHeight into panelWidth and
+ // panelHeight eagerly in order to complete initialization, and
+ // force a reshape later
+ panelWidth = reshapeWidth;
+ panelHeight = reshapeHeight;
+ }
+
+ if (!oglPipelineEnabled) {
+ // Initialize either the hardware-accelerated rendering path or
+ // the lightweight rendering path
+ if (!hardwareAccelerationDisabled) {
+ if (factory.canCreateGLPbuffer()) {
+ if (pbuffer != null) {
+ throw new InternalError("Creating pbuffer twice without destroying it (memory leak / correctness bug)");
+ }
+ try {
+ pbuffer = factory.createGLPbuffer(offscreenCaps,
+ null,
+ pbufferWidth,
+ pbufferHeight,
+ shareWith);
+ updater = new Updater();
+ pbuffer.addGLEventListener(updater);
+ shouldInitialize = false;
+ isInitialized = true;
+ return;
+ } catch (GLException e) {
+ if (DEBUG) {
+ e.printStackTrace();
+ System.err.println("GL2JPanel: Falling back on software rendering because of problems creating pbuffer");
+ }
+ hardwareAccelerationDisabled = true;
+ }
+ } else {
+ if (DEBUG) {
+ System.err.println("GL2JPanel: Falling back on software rendering because no pbuffer support");
+ }
+
+ // If the factory reports that it can't create a pbuffer,
+ // don't try again the next time, and fall through to the
+ // software rendering path
+ hardwareAccelerationDisabled = true;
+ }
+ }
+
+ if (softwareRenderingDisabled) {
+ throw new GLException("Fallback to software rendering disabled by user");
+ }
+
+ // Fall-through path: create an offscreen context instead
+ offscreenDrawable = factory.createOffscreenDrawable(offscreenCaps, chooser);
+ offscreenDrawable.setSize(Math.max(1, panelWidth), Math.max(1, panelHeight));
+ offscreenContext = (GLContextImpl) offscreenDrawable.createContext(shareWith);
+ offscreenContext.setSynchronized(true);
+ }
+ updater = new Updater();
+ shouldInitialize = false;
+ isInitialized = true;
+ }
+
+ private void handleReshape() {
+ readBackWidthInPixels = 0;
+ readBackHeightInPixels = 0;
+
+ panelWidth = reshapeWidth;
+ panelHeight = reshapeHeight;
+
+ if (DEBUG) {
+ System.err.println("GL2JPanel.handleReshape: (w,h) = (" +
+ panelWidth + "," + panelHeight + ")");
+ }
+
+ sendReshape = true;
+
+ if (!oglPipelineEnabled) {
+ if (!hardwareAccelerationDisabled) {
+ // Use factor larger than 2 during shrinks for some hysteresis
+ float shrinkFactor = 2.5f;
+ if ((panelWidth > pbufferWidth ) || (panelHeight > pbufferHeight) ||
+ (panelWidth < (pbufferWidth / shrinkFactor)) || (panelHeight < (pbufferHeight / shrinkFactor))) {
+ if (DEBUG) {
+ System.err.println("Resizing pbuffer from (" + pbufferWidth + ", " + pbufferHeight + ") " +
+ " to fit (" + panelWidth + ", " + panelHeight + ")");
+ }
+ // Must destroy and recreate pbuffer to fit
+ if (pbuffer != null) {
+ // Watch for errors during pbuffer destruction (due to
+ // buggy / bad OpenGL drivers, in particular SiS) and fall
+ // back to software rendering
+ try {
+ pbuffer.destroy();
+ } catch (GLException e) {
+ hardwareAccelerationDisabled = true;
+ if (DEBUG) {
+ System.err.println("WARNING: falling back to software rendering due to bugs in OpenGL drivers");
+ e.printStackTrace();
+ }
+ }
+ }
+ pbuffer = null;
+ isInitialized = false;
+ pbufferWidth = getNextPowerOf2(panelWidth);
+ pbufferHeight = getNextPowerOf2(panelHeight);
+ if (DEBUG && !hardwareAccelerationDisabled) {
+ System.err.println("New pbuffer size is (" + pbufferWidth + ", " + pbufferHeight + ")");
+ }
+ initialize();
+ }
+
+ // It looks like NVidia's drivers (at least the ones on my
+ // notebook) are buggy and don't allow a rectangle of less than
+ // the pbuffer's width to be read...this doesn't really matter
+ // because it's the Graphics.drawImage() calls that are the
+ // bottleneck. Should probably make the size of the offscreen
+ // image be the exact size of the pbuffer to save some work on
+ // resize operations...
+ if (!hardwareAccelerationDisabled) {
+ readBackWidthInPixels = pbufferWidth;
+ readBackHeightInPixels = panelHeight;
+ } else {
+ // Just disabled hardware acceleration during this resize operation; do a fixup
+ readBackWidthInPixels = Math.max(1, panelWidth);
+ readBackHeightInPixels = Math.max(1, panelHeight);
+ }
+ } else {
+ offscreenContext.destroy();
+ offscreenDrawable.setSize(Math.max(1, panelWidth), Math.max(1, panelHeight));
+ readBackWidthInPixels = Math.max(1, panelWidth);
+ readBackHeightInPixels = Math.max(1, panelHeight);
+ }
+
+ if (offscreenImage != null) {
+ offscreenImage.flush();
+ offscreenImage = null;
+ }
+ }
+
+ handleReshape = false;
+ }
+
+ // FIXME: it isn't clear whether this works any more given that
+ // we're accessing the GLDrawable inside of the GLPbuffer directly
+ // up in reshape() -- need to rethink and clean this up
+ class Updater implements GLEventListener {
+ private Graphics g;
+
+ public void setGraphics(Graphics g) {
+ this.g = g;
+ }
+
+ public void init(GLAutoDrawable drawable) {
+ if (oglPipelineEnabled) {
+ if (!preGL(g)) {
+ return;
+ }
+ }
+ drawableHelper.init(GL2JPanel.this);
+ if (oglPipelineEnabled) {
+ postGL(g);
+ }
+ }
+
+ public void display(GLAutoDrawable drawable) {
+ if (oglPipelineEnabled) {
+ if (!preGL(g)) {
+ return;
+ }
+ }
+
+ if (sendReshape) {
+ if (DEBUG) {
+ System.err.println("glViewport(" + viewportX + ", " + viewportY + ", " + panelWidth + ", " + panelHeight + ")");
+ }
+ getGL().getGL2().glViewport(viewportX, viewportY, panelWidth, panelHeight);
+ drawableHelper.reshape(GL2JPanel.this, viewportX, viewportY, panelWidth, panelHeight);
+ sendReshape = false;
+ }
+
+ drawableHelper.display(GL2JPanel.this);
+
+ if (!oglPipelineEnabled) {
+ // Must now copy pixels from offscreen context into surface
+ if (offscreenImage == null) {
+ if (panelWidth > 0 && panelHeight > 0) {
+ // It looks like NVidia's drivers (at least the ones on my
+ // notebook) are buggy and don't allow a sub-rectangle to be
+ // read from a pbuffer...this doesn't really matter because
+ // it's the Graphics.drawImage() calls that are the
+ // bottleneck
+
+ int awtFormat = 0;
+ int hwGLFormat = 0;
+ if (!hardwareAccelerationDisabled) {
+ // This seems to be a good choice on all platforms
+ hwGLFormat = GL2.GL_UNSIGNED_INT_8_8_8_8_REV;
+ }
+
+ // Should be more flexible in these BufferedImage formats;
+ // perhaps see what the preferred image types are on the
+ // given platform
+ if (isOpaque()) {
+ awtFormat = BufferedImage.TYPE_INT_RGB;
+ } else {
+ awtFormat = BufferedImage.TYPE_INT_ARGB;
+ }
+
+ offscreenImage = new BufferedImage(panelWidth,
+ panelHeight,
+ awtFormat);
+ switch (awtFormat) {
+ case BufferedImage.TYPE_3BYTE_BGR:
+ glFormat = GL2.GL_BGR;
+ glType = GL2.GL_UNSIGNED_BYTE;
+ readBackBytes = ByteBuffer.allocate(readBackWidthInPixels * readBackHeightInPixels * 3);
+ break;
+
+ case BufferedImage.TYPE_INT_RGB:
+ case BufferedImage.TYPE_INT_ARGB:
+ glFormat = GL2.GL_BGRA;
+ glType = (hardwareAccelerationDisabled
+ ? offscreenContext.getOffscreenContextPixelDataType()
+ : hwGLFormat);
+ readBackInts = IntBuffer.allocate(readBackWidthInPixels * readBackHeightInPixels);
+ break;
+
+ default:
+ // FIXME: Support more off-screen image types (current
+ // offscreen context implementations don't use others, and
+ // some of the OpenGL formats aren't supported in the 1.1
+ // headers, which we're currently using)
+ throw new GLException("Unsupported offscreen image type " + awtFormat);
+ }
+ }
+ }
+
+ if (offscreenImage != null) {
+ GL2 gl = getGL().getGL2();
+ // Save current modes
+ gl.glGetIntegerv(GL2.GL_PACK_SWAP_BYTES, swapbytes, 0);
+ gl.glGetIntegerv(GL2.GL_PACK_ROW_LENGTH, rowlength, 0);
+ gl.glGetIntegerv(GL2.GL_PACK_SKIP_ROWS, skiprows, 0);
+ gl.glGetIntegerv(GL2.GL_PACK_SKIP_PIXELS, skippixels, 0);
+ gl.glGetIntegerv(GL2.GL_PACK_ALIGNMENT, alignment, 0);
+
+ gl.glPixelStorei(GL2.GL_PACK_SWAP_BYTES, GL2.GL_FALSE);
+ gl.glPixelStorei(GL2.GL_PACK_ROW_LENGTH, readBackWidthInPixels);
+ gl.glPixelStorei(GL2.GL_PACK_SKIP_ROWS, 0);
+ gl.glPixelStorei(GL2.GL_PACK_SKIP_PIXELS, 0);
+ gl.glPixelStorei(GL2.GL_PACK_ALIGNMENT, 1);
+
+ // Actually read the pixels.
+ gl.glReadBuffer(GL2.GL_FRONT);
+ if (readBackBytes != null) {
+ gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, glFormat, glType, readBackBytes);
+ } else if (readBackInts != null) {
+ gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, glFormat, glType, readBackInts);
+ }
+
+ // Restore saved modes.
+ gl.glPixelStorei(GL2.GL_PACK_SWAP_BYTES, swapbytes[0]);
+ gl.glPixelStorei(GL2.GL_PACK_ROW_LENGTH, rowlength[0]);
+ gl.glPixelStorei(GL2.GL_PACK_SKIP_ROWS, skiprows[0]);
+ gl.glPixelStorei(GL2.GL_PACK_SKIP_PIXELS, skippixels[0]);
+ gl.glPixelStorei(GL2.GL_PACK_ALIGNMENT, alignment[0]);
+
+ if (readBackBytes != null || readBackInts != null) {
+ // Copy temporary data into raster of BufferedImage for faster
+ // blitting Note that we could avoid this copy in the cases
+ // where !offscreenContext.offscreenImageNeedsVerticalFlip(),
+ // but that's the software rendering path which is very slow
+ // anyway
+ Object src = null;
+ Object dest = null;
+ int srcIncr = 0;
+ int destIncr = 0;
+
+ if (readBackBytes != null) {
+ src = readBackBytes.array();
+ dest = ((DataBufferByte) offscreenImage.getRaster().getDataBuffer()).getData();
+ srcIncr = readBackWidthInPixels * 3;
+ destIncr = offscreenImage.getWidth() * 3;
+ } else {
+ src = readBackInts.array();
+ dest = ((DataBufferInt) offscreenImage.getRaster().getDataBuffer()).getData();
+ srcIncr = readBackWidthInPixels;
+ destIncr = offscreenImage.getWidth();
+ }
+
+ if (!hardwareAccelerationDisabled ||
+ offscreenContext.offscreenImageNeedsVerticalFlip()) {
+ int srcPos = 0;
+ int destPos = (offscreenImage.getHeight() - 1) * destIncr;
+ for (; destPos >= 0; srcPos += srcIncr, destPos -= destIncr) {
+ System.arraycopy(src, srcPos, dest, destPos, destIncr);
+ }
+ } else {
+ int srcPos = 0;
+ int destEnd = destIncr * offscreenImage.getHeight();
+ for (int destPos = 0; destPos < destEnd; srcPos += srcIncr, destPos += destIncr) {
+ System.arraycopy(src, srcPos, dest, destPos, destIncr);
+ }
+ }
+
+ // Note: image will be drawn back in paintComponent() for
+ // correctness on all platforms
+ }
+ }
+ } else {
+ // Cause OpenGL pipeline to flush its results because
+ // otherwise it's possible we will buffer up multiple frames'
+ // rendering results, resulting in apparent mouse lag
+ GL2 gl = getGL().getGL2();
+ gl.glFinish();
+
+ postGL(g);
+ }
+ }
+
+ public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
+ // This is handled above and dispatched directly to the appropriate context
+ }
+
+ public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {
+ }
+ }
+
+ class InitAction implements Runnable {
+ public void run() {
+ updater.init(GL2JPanel.this);
+ }
+ }
+ private InitAction initAction = new InitAction();
+
+ class DisplayAction implements Runnable {
+ public void run() {
+ updater.display(GL2JPanel.this);
+ }
+ }
+ private DisplayAction displayAction = new DisplayAction();
+
+ // This one is used exclusively in the non-hardware-accelerated case
+ class SwapBuffersAction implements Runnable {
+ public void run() {
+ offscreenDrawable.swapBuffers();
+ }
+ }
+ private SwapBuffersAction swapBuffersAction = new SwapBuffersAction();
+
+ class PaintImmediatelyAction implements Runnable {
+ public void run() {
+ paintImmediately(0, 0, getWidth(), getHeight());
+ }
+ }
+ private PaintImmediatelyAction paintImmediatelyAction = new PaintImmediatelyAction();
+
+ private int getNextPowerOf2(int number) {
+ // Workaround for problems where 0 width or height are transiently
+ // seen during layout
+ if (number == 0) {
+ return 2;
+ }
+
+ if (((number-1) & number) == 0) {
+ //ex: 8 -> 0b1000; 8-1=7 -> 0b0111; 0b1000&0b0111 == 0
+ return number;
+ }
+ int power = 0;
+ while (number > 0) {
+ number = number>>1;
+ power++;
+ }
+ return (1<<power);
+ }
+
+ private int getGLInteger(GL gl, int which) {
+ int[] tmp = new int[1];
+ gl.glGetIntegerv(which, tmp, 0);
+ return tmp[0];
+ }
+}
diff --git a/src/classes/javax/media/opengl/util/Animator.java b/src/classes/javax/media/opengl/util/Animator.java
new file mode 100755
index 000000000..a1a4ca9d4
--- /dev/null
+++ b/src/classes/javax/media/opengl/util/Animator.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ *
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package javax.media.opengl.util;
+
+import java.util.*;
+
+import javax.media.opengl.*;
+
+/** <P> An Animator can be attached to one or more {@link
+ GLAutoDrawable}s to drive their display() methods in a loop. </P>
+
+ <P> The Animator class creates a background thread in which the
+ calls to <code>display()</code> are performed. After each drawable
+ has been redrawn, a brief pause is performed to avoid swamping the
+ CPU, unless {@link #setRunAsFastAsPossible} has been called. </P>
+*/
+
+public class Animator {
+ private volatile ArrayList/*<GLAutoDrawable>*/ drawables = new ArrayList();
+ private Runnable runnable;
+ private boolean runAsFastAsPossible;
+ protected Thread thread;
+ protected volatile boolean shouldStop;
+ protected boolean ignoreExceptions;
+ protected boolean printExceptions;
+
+ /** Creates a new, empty Animator. */
+ public Animator() {
+ }
+
+ /** Creates a new Animator for a particular drawable. */
+ public Animator(GLAutoDrawable drawable) {
+ add(drawable);
+ }
+
+ /** Adds a drawable to the list managed by this Animator. */
+ public synchronized void add(GLAutoDrawable drawable) {
+ ArrayList newList = (ArrayList) drawables.clone();
+ newList.add(drawable);
+ drawables = newList;
+ notifyAll();
+ }
+
+ /** Removes a drawable from the list managed by this Animator. */
+ public synchronized void remove(GLAutoDrawable drawable) {
+ ArrayList newList = (ArrayList) drawables.clone();
+ newList.remove(drawable);
+ drawables = newList;
+ }
+
+ /** Returns an iterator over the drawables managed by this
+ Animator. */
+ public Iterator/*<GLAutoDrawable>*/ drawableIterator() {
+ return drawables.iterator();
+ }
+
+ /** Sets a flag causing this Animator to ignore exceptions produced
+ while redrawing the drawables. By default this flag is set to
+ false, causing any exception thrown to halt the Animator. */
+ public void setIgnoreExceptions(boolean ignoreExceptions) {
+ this.ignoreExceptions = ignoreExceptions;
+ }
+
+ /** Sets a flag indicating that when exceptions are being ignored by
+ this Animator (see {@link #setIgnoreExceptions}), to print the
+ exceptions' stack traces for diagnostic information. Defaults to
+ false. */
+ public void setPrintExceptions(boolean printExceptions) {
+ this.printExceptions = printExceptions;
+ }
+
+ /** Sets a flag in this Animator indicating that it is to run as
+ fast as possible. By default there is a brief pause in the
+ animation loop which prevents the CPU from getting swamped.
+ This method may not have an effect on subclasses. */
+ public final void setRunAsFastAsPossible(boolean runFast) {
+ runAsFastAsPossible = runFast;
+ }
+
+ /** Called every frame to cause redrawing of all of the
+ GLAutoDrawables this Animator manages. Subclasses should call
+ this to get the most optimized painting behavior for the set of
+ components this Animator manages, in particular when multiple
+ lightweight widgets are continually being redrawn. */
+ protected void display() {
+ Iterator iter = drawableIterator();
+ while (iter.hasNext()) {
+ GLAutoDrawable drawable = (GLAutoDrawable) iter.next();
+ try {
+ drawable.display();
+ } catch (RuntimeException e) {
+ if (ignoreExceptions) {
+ if (printExceptions) {
+ e.printStackTrace();
+ }
+ } else {
+ throw(e);
+ }
+ }
+ }
+ }
+
+ class MainLoop implements Runnable {
+ public void run() {
+ try {
+ while (!shouldStop) {
+ // Don't consume CPU unless there is work to be done
+ if (drawables.size() == 0) {
+ synchronized (Animator.this) {
+ while (drawables.size() == 0 && !shouldStop) {
+ try {
+ Animator.this.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+ display();
+ if (!runAsFastAsPossible) {
+ // Avoid swamping the CPU
+ Thread.yield();
+ }
+ }
+ } finally {
+ shouldStop = false;
+ synchronized (Animator.this) {
+ thread = null;
+ Animator.this.notify();
+ }
+ }
+ }
+ }
+
+ /** Starts this animator. */
+ public synchronized void start() {
+ if (thread != null) {
+ throw new GLException("Already started");
+ }
+ if (runnable == null) {
+ runnable = new MainLoop();
+ }
+ thread = new Thread(runnable);
+ thread.start();
+ }
+
+ /** Indicates whether this animator is currently running. This
+ should only be used as a heuristic to applications because in
+ some circumstances the Animator may be in the process of
+ shutting down and this method will still return true. */
+ public synchronized boolean isAnimating() {
+ return (thread != null);
+ }
+
+ /** Stops this animator. In most situations this method blocks until
+ completion, except when called from the animation thread itself
+ or in some cases from an implementation-internal thread like the
+ AWT event queue thread. */
+ public synchronized void stop() {
+ shouldStop = true;
+ notifyAll();
+ // It's hard to tell whether the thread which calls stop() has
+ // dependencies on the Animator's internal thread. Currently we
+ // use a couple of heuristics to determine whether we should do
+ // the blocking wait().
+ if (Thread.currentThread() == thread) {
+ return;
+ }
+ while (shouldStop && thread != null) {
+ try {
+ wait();
+ } catch (InterruptedException ie) {
+ }
+ }
+ }
+
+}
diff --git a/src/classes/javax/media/opengl/util/BufferUtil.java.javame_cdc_fp b/src/classes/javax/media/opengl/util/BufferUtil.java.javame_cdc_fp
new file mode 100755
index 000000000..788016f02
--- /dev/null
+++ b/src/classes/javax/media/opengl/util/BufferUtil.java.javame_cdc_fp
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ *
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package javax.media.opengl.util;
+
+import java.nio.*;
+import java.util.*;
+
+import java.lang.reflect.*;
+
+/** Utility routines for dealing with direct buffers. */
+
+public class BufferUtil {
+ public static final int SIZEOF_BYTE = 1;
+ public static final int SIZEOF_SHORT = 2;
+ public static final int SIZEOF_INT = 4;
+ public static final int SIZEOF_FLOAT = 4;
+
+ private BufferUtil() {}
+
+ //----------------------------------------------------------------------
+ // Allocation routines
+ //
+
+ /** Allocates a new direct ByteBuffer with the specified number of
+ elements. The returned buffer will have its byte order set to
+ the host platform's native byte order. */
+ public static ByteBuffer newByteBuffer(int numElements) {
+ ByteBuffer bb = ByteBuffer.allocateDirect(numElements);
+ nativeOrder(bb);
+ return bb;
+ }
+
+ public static ByteBuffer newByteBuffer(byte[] values) {
+ ByteBuffer bb = newByteBuffer(values.length);
+ bb.put(values);
+ bb.rewind();
+ return bb;
+ }
+
+ /** Allocates a new direct FloatBuffer with the specified number of
+ elements. The returned buffer will have its byte order set to
+ the host platform's native byte order. */
+ public static FloatBuffer newFloatBuffer(int numElements) {
+ ByteBuffer bb = newByteBuffer(numElements * SIZEOF_FLOAT);
+ return bb.asFloatBuffer();
+ }
+
+ public static FloatBuffer newFloatBuffer(float[] values) {
+ FloatBuffer bb = newFloatBuffer(values.length);
+ bb.put(values);
+ bb.rewind();
+ return bb;
+ }
+
+ /** Allocates a new direct IntBuffer with the specified number of
+ elements. The returned buffer will have its byte order set to
+ the host platform's native byte order. */
+ public static IntBuffer newIntBuffer(int numElements) {
+ ByteBuffer bb = newByteBuffer(numElements * SIZEOF_INT);
+ return bb.asIntBuffer();
+ }
+
+ public static IntBuffer newIntBuffer(int[] values) {
+ IntBuffer bb = newIntBuffer(values.length);
+ bb.put(values);
+ bb.rewind();
+ return bb;
+ }
+
+ /** Allocates a new direct ShortBuffer with the specified number of
+ elements. The returned buffer will have its byte order set to
+ the host platform's native byte order. */
+ public static ShortBuffer newShortBuffer(int numElements) {
+ ByteBuffer bb = newByteBuffer(numElements * SIZEOF_SHORT);
+ return bb.asShortBuffer();
+ }
+
+ public static ShortBuffer newShortBuffer(short[] values) {
+ ShortBuffer bb = newShortBuffer(values.length);
+ bb.put(values);
+ bb.rewind();
+ return bb;
+ }
+
+ //----------------------------------------------------------------------
+ // Copy routines (type-to-type)
+ //
+
+ /** Copies the <i>remaining</i> elements (as defined by
+ <code>limit() - position()</code>) in the passed ByteBuffer into
+ a newly-allocated direct ByteBuffer. The returned buffer will
+ have its byte order set to the host platform's native byte
+ order. The position of the newly-allocated buffer will be zero,
+ and the position of the passed buffer is unchanged (though its
+ mark is changed). */
+ public static ByteBuffer copyByteBuffer(ByteBuffer orig) {
+ ByteBuffer dest = newByteBuffer(orig.remaining());
+ dest.put(orig);
+ dest.rewind();
+ return dest;
+ }
+
+ /** Copies the <i>remaining</i> elements (as defined by
+ <code>limit() - position()</code>) in the passed FloatBuffer
+ into a newly-allocated direct FloatBuffer. The returned buffer
+ will have its byte order set to the host platform's native byte
+ order. The position of the newly-allocated buffer will be zero,
+ and the position of the passed buffer is unchanged (though its
+ mark is changed). */
+ public static FloatBuffer copyFloatBuffer(FloatBuffer orig) {
+ return copyFloatBufferAsByteBuffer(orig).asFloatBuffer();
+ }
+
+ /** Copies the <i>remaining</i> elements (as defined by
+ <code>limit() - position()</code>) in the passed IntBuffer
+ into a newly-allocated direct IntBuffer. The returned buffer
+ will have its byte order set to the host platform's native byte
+ order. The position of the newly-allocated buffer will be zero,
+ and the position of the passed buffer is unchanged (though its
+ mark is changed). */
+ public static IntBuffer copyIntBuffer(IntBuffer orig) {
+ return copyIntBufferAsByteBuffer(orig).asIntBuffer();
+ }
+
+ /** Copies the <i>remaining</i> elements (as defined by
+ <code>limit() - position()</code>) in the passed ShortBuffer
+ into a newly-allocated direct ShortBuffer. The returned buffer
+ will have its byte order set to the host platform's native byte
+ order. The position of the newly-allocated buffer will be zero,
+ and the position of the passed buffer is unchanged (though its
+ mark is changed). */
+ public static ShortBuffer copyShortBuffer(ShortBuffer orig) {
+ return copyShortBufferAsByteBuffer(orig).asShortBuffer();
+ }
+
+ //----------------------------------------------------------------------
+ // Copy routines (type-to-ByteBuffer)
+ //
+
+ /** Copies the <i>remaining</i> elements (as defined by
+ <code>limit() - position()</code>) in the passed FloatBuffer
+ into a newly-allocated direct ByteBuffer. The returned buffer
+ will have its byte order set to the host platform's native byte
+ order. The position of the newly-allocated buffer will be zero,
+ and the position of the passed buffer is unchanged (though its
+ mark is changed). */
+ public static ByteBuffer copyFloatBufferAsByteBuffer(FloatBuffer orig) {
+ ByteBuffer dest = newByteBuffer(orig.remaining() * SIZEOF_FLOAT);
+ dest.asFloatBuffer().put(orig);
+ dest.rewind();
+ return dest;
+ }
+
+ /** Copies the <i>remaining</i> elements (as defined by
+ <code>limit() - position()</code>) in the passed IntBuffer into
+ a newly-allocated direct ByteBuffer. The returned buffer will
+ have its byte order set to the host platform's native byte
+ order. The position of the newly-allocated buffer will be zero,
+ and the position of the passed buffer is unchanged (though its
+ mark is changed). */
+ public static ByteBuffer copyIntBufferAsByteBuffer(IntBuffer orig) {
+ ByteBuffer dest = newByteBuffer(orig.remaining() * SIZEOF_INT);
+ dest.asIntBuffer().put(orig);
+ dest.rewind();
+ return dest;
+ }
+
+ /** Copies the <i>remaining</i> elements (as defined by
+ <code>limit() - position()</code>) in the passed ShortBuffer
+ into a newly-allocated direct ByteBuffer. The returned buffer
+ will have its byte order set to the host platform's native byte
+ order. The position of the newly-allocated buffer will be zero,
+ and the position of the passed buffer is unchanged (though its
+ mark is changed). */
+ public static ByteBuffer copyShortBufferAsByteBuffer(ShortBuffer orig) {
+ ByteBuffer dest = newByteBuffer(orig.remaining() * SIZEOF_SHORT);
+ dest.asShortBuffer().put(orig);
+ dest.rewind();
+ return dest;
+ }
+
+ //----------------------------------------------------------------------
+ // Conversion routines
+ //
+
+ public final static float[] getFloatArray(double[] source) {
+ int i=source.length;
+ float[] dest = new float[i--];
+ while(i>=0) { dest[i]=(float)source[i]; i--; }
+ return dest;
+ }
+
+ public final static FloatBuffer getFloatBuffer(DoubleBuffer source) {
+ source.rewind();
+ FloatBuffer dest = BufferUtil.newFloatBuffer(source.limit());
+ while(source.hasRemaining()) { dest.put((float)source.get()); }
+ return dest;
+ }
+
+
+ //----------------------------------------------------------------------
+ // Internals only below this point
+ //
+
+ // NOTE that this work must be done reflectively at the present time
+ // because this code must compile and run correctly on both CDC/FP and J2SE
+ private static boolean isCDCFP;
+ private static Class byteOrderClass;
+ private static Object nativeOrderObject;
+ private static Method orderMethod;
+
+ private static void nativeOrder(ByteBuffer buf) {
+ if (!isCDCFP) {
+ try {
+ if (byteOrderClass == null) {
+ byteOrderClass = Class.forName("java.nio.ByteOrder");
+ orderMethod = ByteBuffer.class.getMethod("order", new Class[] { byteOrderClass });
+ Method nativeOrderMethod = byteOrderClass.getMethod("nativeOrder", null);
+ nativeOrderObject = nativeOrderMethod.invoke(null, null);
+ }
+ } catch (Throwable t) {
+ // Must be running on CDC / FP
+ isCDCFP = true;
+ }
+
+ if (!isCDCFP) {
+ try {
+ orderMethod.invoke(buf, new Object[] { nativeOrderObject });
+ } catch (Throwable t) {
+ }
+ }
+ }
+ }
+}
diff --git a/src/classes/javax/media/opengl/util/BufferUtil.java.javase b/src/classes/javax/media/opengl/util/BufferUtil.java.javase
new file mode 100755
index 000000000..2c49d5fae
--- /dev/null
+++ b/src/classes/javax/media/opengl/util/BufferUtil.java.javase
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ *
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package javax.media.opengl.util;
+
+import java.nio.*;
+import java.util.*;
+
+import java.lang.reflect.*;
+
+/** Utility routines for dealing with direct buffers. */
+
+public class BufferUtil {
+ public static final int SIZEOF_BYTE = 1;
+ public static final int SIZEOF_SHORT = 2;
+ public static final int SIZEOF_INT = 4;
+ public static final int SIZEOF_FLOAT = 4;
+ public static final int SIZEOF_LONG = 8;
+ public static final int SIZEOF_DOUBLE = 8;
+
+ private BufferUtil() {}
+
+ //----------------------------------------------------------------------
+ // Allocation routines
+ //
+
+ /** Allocates a new direct ByteBuffer with the specified number of
+ elements. The returned buffer will have its byte order set to
+ the host platform's native byte order. */
+ public static ByteBuffer newByteBuffer(int numElements) {
+ ByteBuffer bb = ByteBuffer.allocateDirect(numElements);
+ nativeOrder(bb);
+ return bb;
+ }
+
+ public static ByteBuffer newByteBuffer(byte[] values) {
+ ByteBuffer bb = newByteBuffer(values.length);
+ bb.put(values);
+ bb.rewind();
+ return bb;
+ }
+
+ /** Allocates a new direct DoubleBuffer with the specified number of
+ elements. The returned buffer will have its byte order set to
+ the host platform's native byte order. */
+ public static DoubleBuffer newDoubleBuffer(int numElements) {
+ ByteBuffer bb = newByteBuffer(numElements * SIZEOF_DOUBLE);
+ return bb.asDoubleBuffer();
+ }
+
+ /** Allocates a new direct FloatBuffer with the specified number of
+ elements. The returned buffer will have its byte order set to
+ the host platform's native byte order. */
+ public static FloatBuffer newFloatBuffer(int numElements) {
+ ByteBuffer bb = newByteBuffer(numElements * SIZEOF_FLOAT);
+ return bb.asFloatBuffer();
+ }
+
+ public static FloatBuffer newFloatBuffer(float[] values) {
+ FloatBuffer bb = newFloatBuffer(values.length);
+ bb.put(values);
+ bb.rewind();
+ return bb;
+ }
+
+ /** Allocates a new direct IntBuffer with the specified number of
+ elements. The returned buffer will have its byte order set to
+ the host platform's native byte order. */
+ public static IntBuffer newIntBuffer(int numElements) {
+ ByteBuffer bb = newByteBuffer(numElements * SIZEOF_INT);
+ return bb.asIntBuffer();
+ }
+
+ public static IntBuffer newIntBuffer(int[] values) {
+ IntBuffer bb = newIntBuffer(values.length);
+ bb.put(values);
+ bb.rewind();
+ return bb;
+ }
+
+ /** Allocates a new direct LongBuffer with the specified number of
+ elements. The returned buffer will have its byte order set to
+ the host platform's native byte order. */
+ public static LongBuffer newLongBuffer(int numElements) {
+ ByteBuffer bb = newByteBuffer(numElements * SIZEOF_LONG);
+ return bb.asLongBuffer();
+ }
+
+ /** Allocates a new direct ShortBuffer with the specified number of
+ elements. The returned buffer will have its byte order set to
+ the host platform's native byte order. */
+ public static ShortBuffer newShortBuffer(int numElements) {
+ ByteBuffer bb = newByteBuffer(numElements * SIZEOF_SHORT);
+ return bb.asShortBuffer();
+ }
+
+ public static ShortBuffer newShortBuffer(short[] values) {
+ ShortBuffer bb = newShortBuffer(values.length);
+ bb.put(values);
+ bb.rewind();
+ return bb;
+ }
+
+ //----------------------------------------------------------------------
+ // Copy routines (type-to-type)
+ //
+
+ /** Copies the <i>remaining</i> elements (as defined by
+ <code>limit() - position()</code>) in the passed ByteBuffer into
+ a newly-allocated direct ByteBuffer. The returned buffer will
+ have its byte order set to the host platform's native byte
+ order. The position of the newly-allocated buffer will be zero,
+ and the position of the passed buffer is unchanged (though its
+ mark is changed). */
+ public static ByteBuffer copyByteBuffer(ByteBuffer orig) {
+ ByteBuffer dest = newByteBuffer(orig.remaining());
+ dest.put(orig);
+ dest.rewind();
+ return dest;
+ }
+
+ /** Copies the <i>remaining</i> elements (as defined by
+ <code>limit() - position()</code>) in the passed FloatBuffer
+ into a newly-allocated direct FloatBuffer. The returned buffer
+ will have its byte order set to the host platform's native byte
+ order. The position of the newly-allocated buffer will be zero,
+ and the position of the passed buffer is unchanged (though its
+ mark is changed). */
+ public static FloatBuffer copyFloatBuffer(FloatBuffer orig) {
+ return copyFloatBufferAsByteBuffer(orig).asFloatBuffer();
+ }
+
+ /** Copies the <i>remaining</i> elements (as defined by
+ <code>limit() - position()</code>) in the passed IntBuffer
+ into a newly-allocated direct IntBuffer. The returned buffer
+ will have its byte order set to the host platform's native byte
+ order. The position of the newly-allocated buffer will be zero,
+ and the position of the passed buffer is unchanged (though its
+ mark is changed). */
+ public static IntBuffer copyIntBuffer(IntBuffer orig) {
+ return copyIntBufferAsByteBuffer(orig).asIntBuffer();
+ }
+
+ /** Copies the <i>remaining</i> elements (as defined by
+ <code>limit() - position()</code>) in the passed ShortBuffer
+ into a newly-allocated direct ShortBuffer. The returned buffer
+ will have its byte order set to the host platform's native byte
+ order. The position of the newly-allocated buffer will be zero,
+ and the position of the passed buffer is unchanged (though its
+ mark is changed). */
+ public static ShortBuffer copyShortBuffer(ShortBuffer orig) {
+ return copyShortBufferAsByteBuffer(orig).asShortBuffer();
+ }
+
+ //----------------------------------------------------------------------
+ // Copy routines (type-to-ByteBuffer)
+ //
+
+ /** Copies the <i>remaining</i> elements (as defined by
+ <code>limit() - position()</code>) in the passed FloatBuffer
+ into a newly-allocated direct ByteBuffer. The returned buffer
+ will have its byte order set to the host platform's native byte
+ order. The position of the newly-allocated buffer will be zero,
+ and the position of the passed buffer is unchanged (though its
+ mark is changed). */
+ public static ByteBuffer copyFloatBufferAsByteBuffer(FloatBuffer orig) {
+ ByteBuffer dest = newByteBuffer(orig.remaining() * SIZEOF_FLOAT);
+ dest.asFloatBuffer().put(orig);
+ dest.rewind();
+ return dest;
+ }
+
+ /** Copies the <i>remaining</i> elements (as defined by
+ <code>limit() - position()</code>) in the passed IntBuffer into
+ a newly-allocated direct ByteBuffer. The returned buffer will
+ have its byte order set to the host platform's native byte
+ order. The position of the newly-allocated buffer will be zero,
+ and the position of the passed buffer is unchanged (though its
+ mark is changed). */
+ public static ByteBuffer copyIntBufferAsByteBuffer(IntBuffer orig) {
+ ByteBuffer dest = newByteBuffer(orig.remaining() * SIZEOF_INT);
+ dest.asIntBuffer().put(orig);
+ dest.rewind();
+ return dest;
+ }
+
+ /** Copies the <i>remaining</i> elements (as defined by
+ <code>limit() - position()</code>) in the passed ShortBuffer
+ into a newly-allocated direct ByteBuffer. The returned buffer
+ will have its byte order set to the host platform's native byte
+ order. The position of the newly-allocated buffer will be zero,
+ and the position of the passed buffer is unchanged (though its
+ mark is changed). */
+ public static ByteBuffer copyShortBufferAsByteBuffer(ShortBuffer orig) {
+ ByteBuffer dest = newByteBuffer(orig.remaining() * SIZEOF_SHORT);
+ dest.asShortBuffer().put(orig);
+ dest.rewind();
+ return dest;
+ }
+
+ //----------------------------------------------------------------------
+ // Conversion routines
+ //
+
+ public final static float[] getFloatArray(double[] source) {
+ int i=source.length;
+ float[] dest = new float[i--];
+ while(i>=0) { dest[i]=(float)source[i]; i--; }
+ return dest;
+ }
+
+ public final static FloatBuffer getFloatBuffer(DoubleBuffer source) {
+ source.rewind();
+ FloatBuffer dest = BufferUtil.newFloatBuffer(source.limit());
+ while(source.hasRemaining()) { dest.put((float)source.get()); }
+ return dest;
+ }
+
+ //----------------------------------------------------------------------
+ // Internals only below this point
+ //
+
+ // NOTE that this work must be done reflectively at the present time
+ // because this code must compile and run correctly on both CDC/FP and J2SE
+ private static boolean isCDCFP;
+ private static Class byteOrderClass;
+ private static Object nativeOrderObject;
+ private static Method orderMethod;
+
+ private static void nativeOrder(ByteBuffer buf) {
+ if (!isCDCFP) {
+ try {
+ if (byteOrderClass == null) {
+ byteOrderClass = Class.forName("java.nio.ByteOrder");
+ orderMethod = ByteBuffer.class.getMethod("order", new Class[] { byteOrderClass });
+ Method nativeOrderMethod = byteOrderClass.getMethod("nativeOrder", null);
+ nativeOrderObject = nativeOrderMethod.invoke(null, null);
+ }
+ } catch (Throwable t) {
+ // Must be running on CDC / FP
+ isCDCFP = true;
+ }
+
+ if (!isCDCFP) {
+ try {
+ orderMethod.invoke(buf, new Object[] { nativeOrderObject });
+ } catch (Throwable t) {
+ }
+ }
+ }
+ }
+}
diff --git a/src/classes/javax/media/opengl/util/FPSAnimator.java b/src/classes/javax/media/opengl/util/FPSAnimator.java
new file mode 100755
index 000000000..a00a96424
--- /dev/null
+++ b/src/classes/javax/media/opengl/util/FPSAnimator.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ *
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package javax.media.opengl.util;
+
+import java.util.*;
+import javax.media.opengl.*;
+
+/** An Animator subclass which attempts to achieve a target
+ frames-per-second rate to avoid using all CPU time. The target FPS
+ is only an estimate and is not guaranteed. */
+
+public class FPSAnimator extends Animator {
+ private Timer timer;
+ private int fps;
+ private boolean scheduleAtFixedRate;
+
+ /** Creates an FPSAnimator with a given target frames-per-second
+ value. Equivalent to <code>FPSAnimator(null, fps)</code>. */
+ public FPSAnimator(int fps) {
+ this(null, fps);
+ }
+
+ /** Creates an FPSAnimator with a given target frames-per-second
+ value and a flag indicating whether to use fixed-rate
+ scheduling. Equivalent to <code>FPSAnimator(null, fps,
+ scheduleAtFixedRate)</code>. */
+ public FPSAnimator(int fps, boolean scheduleAtFixedRate) {
+ this(null, fps, scheduleAtFixedRate);
+ }
+
+ /** Creates an FPSAnimator with a given target frames-per-second
+ value and an initial drawable to animate. Equivalent to
+ <code>FPSAnimator(null, fps, false)</code>. */
+ public FPSAnimator(GLAutoDrawable drawable, int fps) {
+ this(drawable, fps, false);
+ }
+
+ /** Creates an FPSAnimator with a given target frames-per-second
+ value, an initial drawable to animate, and a flag indicating
+ whether to use fixed-rate scheduling. */
+ public FPSAnimator(GLAutoDrawable drawable, int fps, boolean scheduleAtFixedRate) {
+ this.fps = fps;
+ if (drawable != null) {
+ add(drawable);
+ }
+ this.scheduleAtFixedRate = scheduleAtFixedRate;
+ }
+
+ /** Starts this FPSAnimator. */
+ public synchronized void start() {
+ if (timer != null) {
+ throw new GLException("Already started");
+ }
+ timer = new Timer();
+ long delay = (long) (1000.0f / (float) fps);
+ TimerTask task = new TimerTask() {
+ public void run() {
+ display();
+ }
+ };
+ if (scheduleAtFixedRate) {
+ timer.scheduleAtFixedRate(task, 0, delay);
+ } else {
+ timer.schedule(task, 0, delay);
+ }
+ }
+
+ /** Indicates whether this FPSAnimator is currently running. This
+ should only be used as a heuristic to applications because in
+ some circumstances the FPSAnimator may be in the process of
+ shutting down and this method will still return true. */
+ public synchronized boolean isAnimating() {
+ return (timer != null);
+ }
+
+ /** Stops this FPSAnimator. Due to the implementation of the
+ FPSAnimator it is not guaranteed that the FPSAnimator will be
+ completely stopped by the time this method returns. */
+ public synchronized void stop() {
+ if (timer == null) {
+ throw new GLException("Already stopped");
+ }
+ timer.cancel();
+ timer = null;
+ }
+}
diff --git a/src/classes/javax/media/opengl/util/Gamma.java b/src/classes/javax/media/opengl/util/Gamma.java
new file mode 100755
index 000000000..201144e3f
--- /dev/null
+++ b/src/classes/javax/media/opengl/util/Gamma.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ *
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package javax.media.opengl.util;
+
+import com.sun.opengl.impl.*;
+
+/** Provides control over the primary display's gamma, brightness and
+ contrast controls via the hardware gamma ramp tables. Not
+ supported on all platforms or graphics hardware. <P>
+
+ Thanks to the LWJGL project for illustrating how to access gamma
+ control on the various platforms.
+*/
+
+public class Gamma {
+ private Gamma() {}
+
+ /**
+ * Sets the gamma, brightness, and contrast of the current main
+ * display. This functionality is not available on all platforms and
+ * graphics hardware. Returns true if the settings were successfully
+ * changed, false if not. This method may return false for some
+ * values of the incoming arguments even on hardware which does
+ * support the underlying functionality. <P>
+ *
+ * If this method returns true, the display settings will
+ * automatically be reset to their original values upon JVM exit
+ * (assuming the JVM does not crash); if the user wishes to change
+ * the display settings back to normal ahead of time, use {@link
+ * #resetDisplayGamma resetDisplayGamma}(). It is recommended to
+ * call {@link #resetDisplayGamma resetDisplayGamma} before calling
+ * e.g. <code>System.exit()</code> from the application rather than
+ * rely on the shutdown hook functionality due to inevitable race
+ * conditions and unspecified behavior during JVM teardown. <P>
+ *
+ * This method may be called multiple times during the application's
+ * execution, but calling {@link #resetDisplayGamma
+ * resetDisplayGamma} will only reset the settings to the values
+ * before the first call to this method. <P>
+ *
+ * @param gamma The gamma value, typically > 1.0 (default values
+ * vary, but typically roughly 1.0)
+ * @param brightness The brightness value between -1.0 and 1.0,
+ * inclusive (default values vary, but typically 0)
+ * @param contrast The contrast, greater than 0.0 (default values
+ * vary, but typically 1)
+ * @return true if gamma settings were successfully changed, false
+ * if not
+ * @throws IllegalArgumentException if any of the parameters were
+ * out-of-bounds
+ */
+ public static boolean setDisplayGamma(float gamma, float brightness, float contrast) throws IllegalArgumentException {
+ return GLDrawableFactoryImpl.getFactoryImpl(false).setDisplayGamma(gamma, brightness, contrast);
+ }
+
+ /**
+ * Resets the gamma, brightness and contrast values for the primary
+ * display to their original values before {@link #setDisplayGamma
+ * setDisplayGamma} was called the first time. {@link
+ * #setDisplayGamma setDisplayGamma} must be called before calling
+ * this method or an unspecified exception will be thrown. While it
+ * is not explicitly required that this method be called before
+ * exiting, calling it is recommended because of the inevitable
+ * unspecified behavior during JVM teardown.
+ */
+ public static void resetDisplayGamma() {
+ GLDrawableFactoryImpl.getFactoryImpl(false).resetDisplayGamma();
+ }
+}
diff --git a/src/classes/javax/media/opengl/util/ImmModeSink.java b/src/classes/javax/media/opengl/util/ImmModeSink.java
new file mode 100644
index 000000000..7201cec65
--- /dev/null
+++ b/src/classes/javax/media/opengl/util/ImmModeSink.java
@@ -0,0 +1,318 @@
+
+package javax.media.opengl.util;
+
+import javax.media.opengl.*;
+import java.nio.*;
+import java.util.Iterator;
+import java.util.ArrayList;
+
+public class ImmModeSink {
+
+ public static final boolean DEBUG_BEGIN_END = false;
+ public static final boolean DEBUG_DRAW = false;
+ public static final boolean FLOAT2FIXED = false;
+
+ public static final int GL_QUADS = 0x0007;
+ public static final int GL_QUAD_STRIP = 0x0008;
+ public static final int GL_POLYGON = 0x0009;
+
+ public ImmModeSink(int glDataType, int glDrawUsage,
+ int vComps, int nComps, int cComps, int tComps, int initialSize) {
+
+ vboSet = new VBOSet(glDataType, glDrawUsage, vComps, nComps, cComps, tComps, initialSize);
+ this.vboSetList = new ArrayList();
+ }
+
+ private void destroyList(GL gl) {
+ for(Iterator i=vboSetList.iterator(); i.hasNext() ; ) {
+ ((VBOSet)i.next()).destroy(gl);
+ }
+ vboSetList.clear();
+ }
+
+ public void destroy(GL gl) {
+ destroyList(gl);
+
+ vboSet.destroy(gl);
+ }
+
+ public void reset() {
+ reset(null);
+ }
+
+ public void reset(GL gl) {
+ destroyList(gl);
+ vboSet.reset(gl);
+ }
+
+ public String toString() {
+ return "ImmModeSink[listsz: "+vboSetList.size()+
+ ",\n"+vboSet+
+ "]";
+ }
+
+ public void draw(GL gl, boolean disableBufferAfterDraw) {
+ if(DEBUG_DRAW) {
+ Exception e = new Exception("ImmModeSink.draw(disableBufferAfterDraw: "+disableBufferAfterDraw+"):\n\t"+this);
+ e.printStackTrace();
+ }
+ int n=0;
+ for(Iterator i=vboSetList.iterator(); i.hasNext() ; n++) {
+ ((VBOSet)i.next()).draw(gl, disableBufferAfterDraw, n);
+ }
+ }
+
+ public void glBegin(int mode) {
+ if(DEBUG_BEGIN_END) {
+ Exception e = new Exception("ImmModeSink.glBegin("+vboSet.mode+"):\n\t"+this);
+ e.printStackTrace();
+ }
+ vboSet.modeOrig = mode;
+ switch(mode) {
+ case GL_QUADS:
+ mode=GL.GL_TRIANGLE_STRIP;
+ break;
+ case GL_QUAD_STRIP:
+ mode=GL.GL_TRIANGLE_STRIP;
+ break;
+ case GL_POLYGON:
+ mode=GL.GL_LINES;
+ break;
+ }
+ vboSet.mode = mode;
+ vboSet.checkSeal(false);
+ }
+
+ public final void glEnd(GL gl) {
+ glEnd(gl, true);
+ }
+
+ public void glEnd(GL gl, boolean immediateDraw) {
+ if(DEBUG_BEGIN_END) {
+ Exception e = new Exception("ImmModeSink START glEnd(immediate: "+immediateDraw+"):\n\t"+this);
+ e.printStackTrace();
+ }
+ if(immediateDraw) {
+ vboSet.seal(gl, false);
+ vboSet.draw(gl, true, -1);
+ reset(gl);
+ } else {
+ vboSet.seal(gl, true);
+ vboSetList.add(vboSet);
+ vboSet = vboSet.regenerate();
+ }
+ }
+
+ public final void glVertex2f(float x, float y) {
+ vboSet.glVertex2f(x,y);
+ }
+
+ public final void glVertex3f(float x, float y, float z) {
+ vboSet.glVertex3f(x,y,z);
+ }
+
+ public final void glNormal3f(float x, float y, float z) {
+ vboSet.glNormal3f(x,y,z);
+ }
+
+ public final void glColor3f(float x, float y, float z) {
+ vboSet.glColor3f(x,y,z);
+ }
+
+ public final void glTexCoord2f(float x, float y) {
+ vboSet.glTexCoord2f(x,y);
+ }
+
+ public final void glTexCoord3f(float x, float y, float z) {
+ vboSet.glTexCoord3f(x,y,z);
+ }
+
+ private VBOSet vboSet;
+ private ArrayList vboSetList;
+
+ protected static class VBOSet {
+ protected VBOSet(int glDataType, int glDrawUsage,
+ int vComps, int nComps, int cComps, int tComps, int initialSize) {
+ nComps = 0;
+ tComps = 0;
+ if(FLOAT2FIXED && glDataType==GL.GL_FLOAT) {
+ glDataType=GL.GL_FIXED;
+ }
+ this.glDataType=glDataType;
+ this.glDrawUsage=glDrawUsage;
+ this.vComps=vComps;
+ this.nComps=nComps;
+ this.cComps=cComps;
+ this.tComps=tComps;
+ this.initialSize=initialSize;
+
+ this.vertexVBO = VBOBufferDraw.create(GL2ES1.GL_VERTEX_ARRAY, glDataType, glDrawUsage, vComps, initialSize);
+ this.normalVBO = VBOBufferDraw.create(GL2ES1.GL_NORMAL_ARRAY, glDataType, glDrawUsage, nComps, initialSize);
+ this.colorVBO = VBOBufferDraw.create(GL2ES1.GL_COLOR_ARRAY, glDataType, glDrawUsage, cComps, initialSize);
+ this.texcoordVBO = VBOBufferDraw.create(GL2ES1.GL_TEXTURE_COORD_ARRAY, glDataType, glDrawUsage, tComps, initialSize);
+
+ this.sealed=false;
+ this.mode = -1;
+ this.modeOrig = -1;
+ }
+
+ protected final VBOSet regenerate() {
+ return new VBOSet(glDataType, glDrawUsage, vComps, nComps, cComps, tComps, initialSize);
+ }
+
+ protected void destroy(GL gl) {
+ vertexVBO.destroy(gl);
+ normalVBO.destroy(gl);
+ colorVBO.destroy(gl);
+ texcoordVBO.destroy(gl);
+
+ this.mode = -1;
+ this.modeOrig = -1;
+ this.sealed=false;
+ }
+
+ protected void reset(GL gl) {
+ vertexVBO.reset(gl);
+ normalVBO.reset(gl);
+ colorVBO.reset(gl);
+ texcoordVBO.reset(gl);
+
+ this.mode = -1;
+ this.modeOrig = -1;
+ this.sealed=false;
+ }
+
+ public String toString() {
+ return "VBOSet[mode "+mode+
+ ", modeOrig "+modeOrig+
+ ", sealed "+sealed+
+ ",\n\t vertexVBO "+vertexVBO+
+ ",\n\t normalVBO "+normalVBO+
+ ",\n\t colorVBO "+colorVBO+
+ ",\n\t texcoordVBO "+texcoordVBO+
+ "]";
+ }
+
+ protected void checkSeal(boolean test) throws GLException {
+ if(mode<0) {
+ throw new GLException("No mode set yet, call glBegin(mode) first:\n\t"+this);
+ }
+ if(sealed!=test) {
+ if(test) {
+ throw new GLException("Not Sealed yet, call glEnd() first:\n\t"+this);
+ } else {
+ throw new GLException("Already Sealed, can't modify VBO after glEnd():\n\t"+this);
+ }
+ }
+ }
+
+ protected void rewind() {
+ checkSeal(true);
+
+ vertexVBO.rewind();
+ normalVBO.rewind();
+ colorVBO.rewind();
+ texcoordVBO.rewind();
+ }
+
+ protected void seal(GL gl, boolean disableBufferAfterSeal)
+ {
+ checkSeal(false);
+ sealed = true;
+
+ vertexVBO.seal(gl, disableBufferAfterSeal);
+ normalVBO.seal(gl, disableBufferAfterSeal);
+ colorVBO.seal(gl, disableBufferAfterSeal);
+ texcoordVBO.seal(gl, disableBufferAfterSeal);
+ }
+
+ protected void draw(GL gl, boolean disableBufferAfterDraw, int i)
+ {
+ if(DEBUG_DRAW) {
+ Exception e = new Exception("ImmModeSink.draw["+i+"](disableBufferAfterDraw: "+disableBufferAfterDraw+"):\n\t"+this);
+ e.printStackTrace();
+ }
+ vertexVBO.enableBuffer(gl);
+ normalVBO.enableBuffer(gl);
+ colorVBO.enableBuffer(gl);
+ texcoordVBO.enableBuffer(gl);
+
+ if (vertexVBO.getBuffer()!=null) {
+ gl.glDrawArrays(mode, 0, vertexVBO.getVerticeNumber());
+ }
+
+ if(disableBufferAfterDraw) {
+ vertexVBO.disableBuffer(gl);
+ normalVBO.disableBuffer(gl);
+ colorVBO.disableBuffer(gl);
+ texcoordVBO.disableBuffer(gl);
+ }
+ }
+
+ protected void glVertex2f(float x, float y) {
+ checkSeal(false);
+ vertexVBO.putf(x);
+ if(vertexVBO.getComponents()>1)
+ vertexVBO.putf(y);
+ vertexVBO.padding(2);
+ }
+
+ protected void glVertex3f(float x, float y, float z) {
+ checkSeal(false);
+ vertexVBO.putf(x);
+ if(vertexVBO.getComponents()>1)
+ vertexVBO.putf(y);
+ if(vertexVBO.getComponents()>2)
+ vertexVBO.putf(z);
+ vertexVBO.padding(3);
+ }
+
+ protected void glNormal3f(float x, float y, float z) {
+ checkSeal(false);
+ normalVBO.putf(x);
+ if(normalVBO.getComponents()>1)
+ normalVBO.putf(y);
+ if(normalVBO.getComponents()>2)
+ normalVBO.putf(z);
+ normalVBO.padding(3);
+ }
+
+ protected void glColor3f(float x, float y, float z) {
+ checkSeal(false);
+ colorVBO.putf(x);
+ if(colorVBO.getComponents()>1)
+ colorVBO.putf(y);
+ if(colorVBO.getComponents()>2)
+ colorVBO.putf(z);
+ colorVBO.padding(3);
+ }
+
+ protected void glTexCoord2f(float x, float y) {
+ checkSeal(false);
+ texcoordVBO.putf(x);
+ if(texcoordVBO.getComponents()>1)
+ texcoordVBO.putf(y);
+ texcoordVBO.padding(2);
+ }
+
+ protected void glTexCoord3f(float x, float y, float z) {
+ checkSeal(false);
+ texcoordVBO.putf(x);
+ if(texcoordVBO.getComponents()>1)
+ texcoordVBO.putf(y);
+ if(texcoordVBO.getComponents()>2)
+ texcoordVBO.putf(z);
+ texcoordVBO.padding(3);
+ }
+
+ VBOBufferDraw vertexVBO;
+ VBOBufferDraw normalVBO;
+ VBOBufferDraw colorVBO;
+ VBOBufferDraw texcoordVBO;
+ int mode, modeOrig;
+ int glDataType, glDrawUsage, vComps, nComps, cComps, tComps, initialSize;
+ boolean sealed;
+ }
+
+}
+
diff --git a/src/classes/javax/media/opengl/util/VBOBufferDraw.java b/src/classes/javax/media/opengl/util/VBOBufferDraw.java
new file mode 100644
index 000000000..7a7a3d9a6
--- /dev/null
+++ b/src/classes/javax/media/opengl/util/VBOBufferDraw.java
@@ -0,0 +1,379 @@
+
+package javax.media.opengl.util;
+
+import javax.media.opengl.*;
+import javax.media.opengl.util.gl2es1.VBOBufferDrawGL2ES1;
+import java.nio.*;
+
+public abstract class VBOBufferDraw {
+
+ public static VBOBufferDraw create(int glArrayType, int glDataType, int glBufferUsage, int comps, int initialSize)
+ throws GLException
+ {
+ if(GLProfile.isGL2ES1()) {
+ return new VBOBufferDrawGL2ES1(glArrayType, glDataType, glBufferUsage, comps, initialSize);
+ }
+ throw new GLException("VBOBufferDraw not supported for profile: "+GLProfile.getProfile());
+ }
+
+ protected void init(int glArrayType, int glDataType, int glBufferUsage, int comps, int initialSize)
+ throws GLException
+ {
+ switch(glArrayType) {
+ case GL2ES1.GL_VERTEX_ARRAY:
+ case GL2ES1.GL_NORMAL_ARRAY:
+ case GL2ES1.GL_COLOR_ARRAY:
+ case GL2ES1.GL_TEXTURE_COORD_ARRAY:
+ break;
+ default:
+ throw new GLException("invalid glArrayType: "+glArrayType+":\n\t"+this);
+ }
+ this.glArrayType = glArrayType;
+ this.glDataType = glDataType;
+ this.clazz = getBufferClass(glDataType);
+ this.buffer = null;
+ this.components = comps;
+ this.initialSize = initialSize;
+ if( ! (GLProfile.isGL2ES2() && glBufferUsage==GL2ES2.GL_STREAM_DRAW) ) {
+ switch(glBufferUsage) {
+ case GL2ES1.GL_STATIC_DRAW:
+ case GL2ES1.GL_DYNAMIC_DRAW:
+ break;
+ default:
+ throw new GLException("invalid glBufferUsage: "+glBufferUsage+":\n\t"+this);
+ }
+ }
+ this.glBufferUsage = glBufferUsage;
+ this.vboName = 0;
+ this.sealed=false;
+ this.bufferEnabled=false;
+ growVBO(initialSize);
+ }
+
+ public int getGLArrayType() {
+ return glArrayType;
+ }
+
+ public int getGlDataType() {
+ return glDataType;
+ }
+
+ public int getComponents() {
+ return components;
+ }
+
+ public Class getBufferClass() {
+ return clazz;
+ }
+
+ public Buffer getBuffer() {
+ return buffer;
+ }
+
+ public int getBufferUsage() {
+ return glBufferUsage;
+ }
+
+ public void destroy(GL gl) {
+ reset(gl);
+ if(vboName!=0) {
+ int[] tmp = new int[1];
+ tmp[0] = vboName;
+ gl.glDeleteBuffers(1, tmp, 0);
+ vboName = 0;
+ }
+ }
+
+ public void reset() {
+ reset(null);
+ }
+
+ public void reset(GL gl) {
+ if(gl!=null) {
+ disableBuffer(gl);
+ }
+ this.sealed=false;
+ if(buffer!=null) {
+ buffer.clear();
+ }
+ }
+
+ private final void init_vbo(GL gl) {
+ if(vboName==0) {
+ int[] tmp = new int[1];
+ gl.glGenBuffers(1, tmp, 0);
+ vboName = tmp[0];
+ }
+ }
+
+ private final void checkSeal(boolean test) throws GLException {
+ if(sealed!=test) {
+ if(test) {
+ throw new GLException("Not Sealed yet, seal first:\n\t"+this);
+ } else {
+ throw new GLException("Already Sealed, can't modify VBO:\n\t"+this);
+ }
+ }
+ }
+
+ public final boolean growVBOIfNecessary(int spare) {
+ if(buffer==null) {
+ throw new GLException("buffer no configured:\n\t"+this);
+ }
+ if(buffer!=null && buffer.remaining()<spare) {
+ growVBO();
+ return true;
+ }
+ return false;
+ }
+
+ public final void growVBO() {
+ growVBO(initialSize);
+ }
+
+ public static final Class getBufferClass(int glDataType) {
+ switch(glDataType) {
+ case GL2ES1.GL_BYTE:
+ case GL2ES1.GL_UNSIGNED_BYTE:
+ return ByteBuffer.class;
+ case GL2ES1.GL_SHORT:
+ case GL2ES1.GL_UNSIGNED_SHORT:
+ return ShortBuffer.class;
+ case GL2ES1.GL_FIXED:
+ return IntBuffer.class;
+ case GL2ES1.GL_FLOAT:
+ return FloatBuffer.class;
+ default:
+ throw new GLException("Given OpenGL data type not supported: "+glDataType);
+ }
+ }
+
+ public final int getBufferCompSize() {
+ if(clazz==ByteBuffer.class) {
+ return BufferUtil.SIZEOF_BYTE;
+ }
+ if(clazz==ShortBuffer.class) {
+ return BufferUtil.SIZEOF_SHORT;
+ }
+ if(clazz==IntBuffer.class) {
+ return BufferUtil.SIZEOF_INT;
+ }
+ if(clazz==FloatBuffer.class) {
+ return BufferUtil.SIZEOF_FLOAT;
+ }
+ throw new GLException("Given Buffer Class not supported: "+clazz+":\n\t"+this);
+ }
+
+ public final void growVBO(int additional) {
+ int osize;
+
+ checkSeal(false);
+
+ if(components>0) {
+ osize = (buffer!=null)?buffer.capacity():0;
+ if(clazz==ByteBuffer.class) {
+ ByteBuffer newBBuffer = BufferUtil.newByteBuffer( (osize+additional) * components );
+ if(buffer!=null) {
+ buffer.flip();
+ newBBuffer.put((ByteBuffer)buffer);
+ }
+ buffer = newBBuffer;
+ } else if(clazz==ShortBuffer.class) {
+ ShortBuffer newSBuffer = BufferUtil.newShortBuffer( (osize+additional) * components );
+ if(buffer!=null) {
+ buffer.flip();
+ newSBuffer.put((ShortBuffer)buffer);
+ }
+ buffer = newSBuffer;
+ } else if(clazz==IntBuffer.class) {
+ IntBuffer newIBuffer = BufferUtil.newIntBuffer( (osize+additional) * components );
+ if(buffer!=null) {
+ buffer.flip();
+ newIBuffer.put((IntBuffer)buffer);
+ }
+ buffer = newIBuffer;
+ } else if(clazz==FloatBuffer.class) {
+ FloatBuffer newFBuffer = BufferUtil.newFloatBuffer( (osize+additional) * components );
+ if(buffer!=null) {
+ buffer.flip();
+ newFBuffer.put((FloatBuffer)buffer);
+ }
+ buffer = newFBuffer;
+ } else {
+ throw new GLException("Given Buffer Class not supported: "+clazz+":\n\t"+this);
+ }
+ }
+ }
+
+ public void rewind() {
+ checkSeal(true);
+
+ if(buffer!=null) {
+ buffer.rewind();
+ }
+ }
+
+ public int getVerticeNumber() {
+ return ( buffer!=null ) ? ( buffer.limit() / components ) : 0 ;
+ }
+
+ public void seal(GL gl, boolean disableAfterSeal)
+ {
+ checkSeal(false);
+ sealed = true;
+ init_vbo(gl);
+
+ if (null!=buffer) {
+ buffer.flip();
+ enableBuffer(gl, true);
+ }
+ if(null==buffer || disableAfterSeal) {
+ disableBuffer(gl);
+ }
+
+ }
+
+ public void enableBuffer(GL gl)
+ {
+ enableBuffer(gl, false);
+ }
+
+ private void enableBuffer(GL gl, boolean newData)
+ {
+ checkSeal(true);
+ enableBufferGLImpl(gl, newData);
+ }
+
+ protected abstract void enableBufferGLImpl(GL gl, boolean newData);
+
+ public void disableBuffer(GL gl) {
+ disableBufferGLImpl(gl);
+ }
+
+ protected abstract void disableBufferGLImpl(GL gl) ;
+
+ public void padding(int done) {
+ if(buffer==null) return; // JAU
+ if(buffer==null) {
+ throw new GLException("buffer no configured:\n\t"+this);
+ }
+ while(done<components) {
+ if(clazz==ByteBuffer.class) {
+ ((ByteBuffer)buffer).put((byte)0);
+ } else if(clazz==ShortBuffer.class) {
+ ((ShortBuffer)buffer).put((short)0);
+ } else if(clazz==IntBuffer.class) {
+ ((IntBuffer)buffer).put(0);
+ } else if(clazz==FloatBuffer.class) {
+ ((FloatBuffer)buffer).put(0f);
+ } else {
+ throw new GLException("Given Buffer Class not supported: "+clazz+" :\n\t"+this);
+ }
+ done++;
+ }
+ }
+
+ public void putb(byte v) {
+ if(buffer==null) return; // JAU
+ growVBOIfNecessary(1);
+ if(clazz==ByteBuffer.class) {
+ ((ByteBuffer)buffer).put(v);
+ } else if(clazz==ShortBuffer.class) {
+ ((ShortBuffer)buffer).put((short)v);
+ } else if(clazz==IntBuffer.class) {
+ ((IntBuffer)buffer).put((int)v);
+ } else {
+ throw new GLException("Byte doesn't match Buffer Class: "+clazz+" :\n\t"+this);
+ }
+ }
+
+ public void puts(short v) {
+ if(buffer==null) return; // JAU
+ growVBOIfNecessary(1);
+ if(clazz==ShortBuffer.class) {
+ ((ShortBuffer)buffer).put(v);
+ } else if(clazz==IntBuffer.class) {
+ ((IntBuffer)buffer).put((int)v);
+ } else {
+ throw new GLException("Short doesn't match Buffer Class: "+clazz+" :\n\t"+this);
+ }
+ }
+
+ public void puti(int v) {
+ if(buffer==null) return; // JAU
+ growVBOIfNecessary(1);
+ if(clazz==IntBuffer.class) {
+ ((IntBuffer)buffer).put(v);
+ } else {
+ throw new GLException("Integer doesn't match Buffer Class: "+clazz+" :\n\t"+this);
+ }
+ }
+
+ public void putx(int v) {
+ if(buffer==null) return; // JAU
+ growVBOIfNecessary(1);
+ if(clazz==IntBuffer.class) {
+ ((IntBuffer)buffer).put(v);
+ } else {
+ throw new GLException("Fixed doesn't match Buffer Class: "+clazz+" :\n\t"+this);
+ }
+ }
+
+ public void putf(float v) {
+ if(buffer==null) return; // JAU
+ growVBOIfNecessary(1);
+ if(clazz==FloatBuffer.class) {
+ ((FloatBuffer)buffer).put(v);
+ } else if(clazz==IntBuffer.class) {
+ ((IntBuffer)buffer).put(Float2Fixed(v));
+ } else {
+ throw new GLException("Float doesn't match Buffer Class: "+clazz+" :\n\t"+this);
+ }
+ }
+
+ public void putd(double v) {
+ if(buffer==null) return; // JAU
+ growVBOIfNecessary(1);
+ if(clazz==FloatBuffer.class) {
+ // FIXME: ok ?
+ ((FloatBuffer)buffer).put((float)v);
+ } else {
+ throw new GLException("Double doesn't match Buffer Class: "+clazz+" :\n\t"+this);
+ }
+ }
+
+ public String toString() {
+ return "VBOBufferDraw[vertices "+getVerticeNumber()+
+ ", glArrayType "+glArrayType+
+ ", glDataType "+glDataType+
+ ", bufferClazz "+clazz+
+ ", components "+components+
+ ", initialSize "+initialSize+
+ ", glBufferUsage "+glBufferUsage+
+ ", vboName "+vboName+
+ ", sealed "+sealed+
+ ", bufferEnabled "+bufferEnabled+
+ ",\n\tbuffer "+buffer+
+ "]";
+ }
+
+ public static final int Float2Fixed(float value)
+ {
+ if (value < -32768) value = -32768;
+ if (value > 32767) value = 32767;
+ return (int)(value * 65536);
+ }
+
+ protected int glArrayType;
+ protected int glDataType;
+ protected Class clazz;
+ protected Buffer buffer;
+ protected int components;
+ protected int initialSize;
+ protected int glBufferUsage;
+ protected int vboName;
+ protected boolean sealed;
+ protected boolean bufferEnabled;
+
+}
+
diff --git a/src/classes/javax/media/opengl/util/gl2es1/VBOBufferDrawGL2ES1.java b/src/classes/javax/media/opengl/util/gl2es1/VBOBufferDrawGL2ES1.java
new file mode 100644
index 000000000..fbca6b569
--- /dev/null
+++ b/src/classes/javax/media/opengl/util/gl2es1/VBOBufferDrawGL2ES1.java
@@ -0,0 +1,51 @@
+
+package javax.media.opengl.util.gl2es1;
+
+import javax.media.opengl.util.VBOBufferDraw;
+import javax.media.opengl.*;
+import java.nio.*;
+
+public class VBOBufferDrawGL2ES1 extends VBOBufferDraw {
+
+ public VBOBufferDrawGL2ES1(int glArrayType, int glDataType, int glBufferUsage, int comps, int initialSize) {
+ init(glArrayType, glDataType, glBufferUsage, comps, initialSize);
+ }
+
+ protected void enableBufferGLImpl(GL _gl, boolean newData) {
+ GL2ES1 gl = _gl.getGL2ES1();
+ if(!bufferEnabled && null!=buffer) {
+ gl.glEnableClientState(glArrayType);
+ gl.glBindBuffer(GL2ES1.GL_ARRAY_BUFFER, vboName);
+ if(newData) {
+ gl.glBufferData(GL2ES1.GL_ARRAY_BUFFER, buffer.limit() * getBufferCompSize(), buffer, glBufferUsage);
+ }
+ switch(glArrayType) {
+ case GL2ES1.GL_VERTEX_ARRAY:
+ gl.glVertexPointer(components, glDataType, 0, 0);
+ break;
+ case GL2ES1.GL_NORMAL_ARRAY:
+ gl.glNormalPointer(components, glDataType, 0);
+ break;
+ case GL2ES1.GL_COLOR_ARRAY:
+ gl.glColorPointer(components, glDataType, 0, 0);
+ break;
+ case GL2ES1.GL_TEXTURE_COORD_ARRAY:
+ gl.glTexCoordPointer(components, glDataType, 0, 0);
+ break;
+ default:
+ throw new GLException("invalid glArrayType: "+glArrayType+":\n\t"+this);
+ }
+ bufferEnabled = true;
+ }
+ }
+
+ protected void disableBufferGLImpl(GL _gl) {
+ GL2ES1 gl = _gl.getGL2ES1();
+ if(bufferEnabled && null!=buffer) {
+ gl.glDisableClientState(glArrayType);
+ bufferEnabled = false;
+ }
+ }
+
+}
+
diff --git a/src/classes/javax/media/opengl/util/swing/JAnimator.java b/src/classes/javax/media/opengl/util/swing/JAnimator.java
new file mode 100755
index 000000000..2b759814d
--- /dev/null
+++ b/src/classes/javax/media/opengl/util/swing/JAnimator.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ *
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package javax.media.opengl.util.swing;
+
+import java.awt.Component;
+import java.awt.EventQueue;
+import java.awt.Rectangle;
+import java.util.*;
+import javax.swing.*;
+
+import javax.media.opengl.*;
+import javax.media.opengl.util.*;
+
+/** <P> An Animator can be attached to one or more {@link
+ GLAutoDrawable}s to drive their display() methods in a loop. </P>
+
+ <P> The Animator class creates a background thread in which the
+ calls to <code>display()</code> are performed. After each drawable
+ has been redrawn, a brief pause is performed to avoid swamping the
+ CPU, unless {@link #setRunAsFastAsPossible} has been called. </P>
+*/
+
+public class JAnimator extends Animator {
+ // For efficient rendering of Swing components, in particular when
+ // they overlap one another
+ private List lightweights = new ArrayList();
+ private Map repaintManagers = new IdentityHashMap();
+ private Map dirtyRegions = new IdentityHashMap();
+
+ /** Creates a new, empty Animator. */
+ public JAnimator() {
+ super();
+ }
+
+ /** Creates a new Animator for a particular drawable. */
+ public JAnimator(GLAutoDrawable drawable) {
+ super(drawable);
+ }
+
+ /** Called every frame to cause redrawing of all of the
+ GLAutoDrawables this Animator manages. Subclasses should call
+ this to get the most optimized painting behavior for the set of
+ components this Animator manages, in particular when multiple
+ lightweight widgets are continually being redrawn. */
+ protected void display() {
+ Iterator iter = drawableIterator();
+ while (iter.hasNext()) {
+ GLAutoDrawable drawable = (GLAutoDrawable) iter.next();
+ if (drawable instanceof JComponent) {
+ // Lightweight components need a more efficient drawing
+ // scheme than simply forcing repainting of each one in
+ // turn since drawing one can force another one to be
+ // drawn in turn
+ lightweights.add(drawable);
+ } else {
+ try {
+ drawable.display();
+ } catch (RuntimeException e) {
+ if (ignoreExceptions) {
+ if (printExceptions) {
+ e.printStackTrace();
+ }
+ } else {
+ throw(e);
+ }
+ }
+ }
+ }
+ if (lightweights.size() > 0) {
+ try {
+ SwingUtilities.invokeAndWait(drawWithRepaintManagerRunnable);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ lightweights.clear();
+ }
+ }
+
+ /** Stops this animator. In most situations this method blocks until
+ completion, except when called from the animation thread itself
+ or in some cases from an implementation-internal thread like the
+ AWT event queue thread. */
+ public synchronized void stop() {
+ shouldStop = true;
+ notifyAll();
+ // It's hard to tell whether the thread which calls stop() has
+ // dependencies on the Animator's internal thread. Currently we
+ // use a couple of heuristics to determine whether we should do
+ // the blocking wait().
+ if ((Thread.currentThread() == thread) || EventQueue.isDispatchThread()) {
+ return;
+ }
+ while (shouldStop && thread != null) {
+ try {
+ wait();
+ } catch (InterruptedException ie) {
+ }
+ }
+ }
+
+ // Uses RepaintManager APIs to implement more efficient redrawing of
+ // the Swing widgets we're animating
+ private Runnable drawWithRepaintManagerRunnable = new Runnable() {
+ public void run() {
+ for (Iterator iter = lightweights.iterator(); iter.hasNext(); ) {
+ JComponent comp = (JComponent) iter.next();
+ RepaintManager rm = RepaintManager.currentManager(comp);
+ rm.markCompletelyDirty(comp);
+ repaintManagers.put(rm, rm);
+
+ // RepaintManagers don't currently optimize the case of
+ // overlapping sibling components. If we have two
+ // JInternalFrames in a JDesktopPane, the redraw of the
+ // bottom one will cause the top one to be redrawn as
+ // well. The top one will then be redrawn separately. In
+ // order to optimize this case we need to compute the union
+ // of all of the dirty regions on a particular JComponent if
+ // optimized drawing isn't enabled for it.
+
+ // Walk up the hierarchy trying to find a non-optimizable
+ // ancestor
+ Rectangle visible = comp.getVisibleRect();
+ int x = visible.x;
+ int y = visible.y;
+ while (comp != null) {
+ x += comp.getX();
+ y += comp.getY();
+ Component c = comp.getParent();
+ if ((c == null) || (!(c instanceof JComponent))) {
+ comp = null;
+ } else {
+ comp = (JComponent) c;
+ if (!comp.isOptimizedDrawingEnabled()) {
+ rm = RepaintManager.currentManager(comp);
+ repaintManagers.put(rm, rm);
+ // Need to dirty this region
+ Rectangle dirty = (Rectangle) dirtyRegions.get(comp);
+ if (dirty == null) {
+ dirty = new Rectangle(x, y, visible.width, visible.height);
+ dirtyRegions.put(comp, dirty);
+ } else {
+ // Compute union with already dirty region
+ // Note we could compute multiple non-overlapping
+ // regions: might want to do that in the future
+ // (prob. need more complex algorithm -- dynamic
+ // programming?)
+ dirty.add(new Rectangle(x, y, visible.width, visible.height));
+ }
+ }
+ }
+ }
+ }
+
+ // Dirty any needed regions on non-optimizable components
+ for (Iterator iter = dirtyRegions.keySet().iterator(); iter.hasNext(); ) {
+ JComponent comp = (JComponent) iter.next();
+ Rectangle rect = (Rectangle) dirtyRegions.get(comp);
+ RepaintManager rm = RepaintManager.currentManager(comp);
+ rm.addDirtyRegion(comp, rect.x, rect.y, rect.width, rect.height);
+ }
+
+ // Draw all dirty regions
+ for (Iterator iter = repaintManagers.keySet().iterator(); iter.hasNext(); ) {
+ ((RepaintManager) iter.next()).paintDirtyRegions();
+ }
+ dirtyRegions.clear();
+ repaintManagers.clear();
+ }
+ };
+}
diff --git a/src/classes/javax/media/opengl/util/swing/JOGLAppletLauncher.java b/src/classes/javax/media/opengl/util/swing/JOGLAppletLauncher.java
new file mode 100755
index 000000000..7a5e29174
--- /dev/null
+++ b/src/classes/javax/media/opengl/util/swing/JOGLAppletLauncher.java
@@ -0,0 +1,1080 @@
+/* This java class is distributed under the BSD license.
+ *
+ * Copyright 2005 Lilian Chamontin.
+ * contact lilian.chamontin at f r e e . f r
+ */
+
+/*
+ * Portions Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ */
+
+package javax.media.opengl.util.swing;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Label;
+import java.awt.Panel;
+import java.applet.Applet;
+import java.applet.AppletStub;
+import java.applet.AppletContext;
+import java.io.*;
+import java.net.*;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.*;
+import java.text.*;
+import java.util.*;
+import java.util.jar.*;
+import javax.swing.*;
+
+import javax.media.opengl.*;
+
+
+/** This class enables deployment of high-end applets which use OpenGL
+ * for 3D graphics via JOGL and (optionally) OpenAL for spatialized
+ * audio via JOAL. The applet being deployed may be either signed or
+ * unsigned; if it is unsigned, it runs inside the security sandbox,
+ * and if it is signed, the user receives a security dialog to accept
+ * the certificate for the applet as well as for JOGL and JOAL. <P>
+ *
+ * The steps for deploying such applets are straightforward. First,
+ * the "archive" parameter to the applet tag must contain jogl.jar
+ * and gluegen-rt.jar, as well as any jar files associated with your
+ * applet (in this case, "your_applet.jar"). <P>
+ *
+ * Second, the codebase directory on the server, which contains the
+ * applet's jar files, must also contain jogl.jar, gluegen-rt.jar,
+ * and all of the jogl-natives-*.jar and gluegen-rt-natives-*.jar
+ * files from the standard JOGL and GlueGen runtime distributions
+ * (provided in jogl-[version]-webstart.zip from the <a
+ * href="http://jogl.dev.java.net/servlets/ProjectDocumentList">JOGL
+ * release builds</a> and gluegen-rt-[version]-webstart.zip from the
+ * <a
+ * href="http://gluegen.dev.java.net/servlets/ProjectDocumentList">GlueGen
+ * runtime release builds</a>). Note that the codebase of the applet
+ * is currently the location from which the JOGL native library used
+ * by the applet is downloaded. All of the JOGL and GlueGen-related
+ * jars must be signed by the same entity, which is typically Sun
+ * Microsystems, Inc. <P>
+ *
+ * To deploy an applet using both JOGL and JOAL, simply add joal.jar
+ * to the list of jars in the archive tag of the applet, and put
+ * joal.jar and the joal-natives-*.jar signed jars into the same
+ * codebase directory on the web server. These signed jars are
+ * supplied in the joal-[version]-webstart.zip archive from the <a
+ * href="http://joal.dev.java.net/servlets/ProjectDocumentList">JOAL
+ * release builds</a>. <P>
+ *
+ * Sample applet code:
+ * <pre>
+ * &lt;applet code="com.sun.opengl.util.JOGLAppletLauncher"
+ * width=600
+ * height=400
+ * codebase="/lib"
+ * archive="jogl.jar,gluegen-rt.jar,your_applet.jar"&gt;
+ * &lt;param name="subapplet.classname" VALUE="untrusted.JOGLApplet"&gt;
+ * &lt;param name="subapplet.displayname" VALUE="My JOGL Applet"&gt;
+ * &lt;param name="progressbar" value="true"&gt;
+ * &lt;param name="cache_archive" VALUE="jogl.jar,gluegen-rt.jar,your_applet.jar"&gt;
+ * &lt;param name="cache_archive_ex" VALUE="jogl.jar;preload,gluegen-rt.jar;preload,your_applet.jar;preload"&gt;
+ * &lt;/applet&gt;
+ * </pre>
+ * <p>
+ *
+ * There are some limitations with this approach. It is not possible
+ * to specify e.g. -Dsun.java2d.noddraw=true or
+ * -Dsun.java2d.opengl=true for better control over the Java2D
+ * pipeline as it is with Java Web Start. However, the
+ * JOGLAppletLauncher tries to force the use of
+ * -Dsun.java2d.noddraw=true on Windows platforms for best robustness
+ * by detecting if it has not been set and asking the user whether it
+ * can update the Java Plug-In configuration automatically. If the
+ * user agrees to this, a browser restart is required in order for the
+ * change to take effect, though it is permanent for subsequent
+ * browser restarts. <P>
+ *
+ * The behavior of the noddraw-related dialog box can be changed via
+ * two applet parameters. The <CODE>jogl.silent.noddraw.check</CODE>
+ * parameter, if set to <CODE>"true"</CODE>, silences the two dialog
+ * boxes associated with this check, forcing it to always be performed
+ * and deployment.properties to be silently updated if necessary
+ * (unless the user previously saw such a dialog box and dismissed it
+ * by saying "No, Don't Ask Again"). The noddraw check can be disabled
+ * completely by setting the <CODE>jogl.disable.noddraw.check</CODE>
+ * applet parameter to <CODE>"true"</CODE>. <P>
+ *
+ * The JOGL (and optionally JOAL) natives are cached in the user's
+ * home directory (the value of the "user.home" system property in
+ * Java) under the directory .jogl_ext. The Java Plug-In is
+ * responsible for performing all other jar caching. If the JOGL
+ * installation is updated on the server, the .jogl_ext cache will
+ * automatically be updated. <p>
+ *
+ * This technique requires that JOGL has not been installed in to the
+ * JRE under e.g. jre/lib/ext. If problems are seen when deploying
+ * this applet launcher, the first question to ask the end user is
+ * whether jogl.jar and any associated DLLs, .so's, etc. are installed
+ * directly in to the JRE. The applet launcher has been tested
+ * primarily under Mozilla, Firefox and Internet Explorer; there may
+ * be problems when running under, for example, Opera. <p>
+ *
+ * It has been discovered that the Talkback agent in Mozilla / Firefox
+ * has bad interactions with OpenGL applets. For highest performance,
+ * we recommend disabling the Talkback agent; find talkback.exe, run
+ * it, and follow the directions for turning it off. Please see
+ * <a href="http://www.javagaming.org/forums/index.php?topic=12200.30">this
+ * thread</a> on the javagaming.org forums and
+ * <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=326381">this
+ * thread</a> on the Mozilla bug reporting database. <p>
+ *
+ * @author Lilian Chamontin
+ * @author Kenneth Russell
+ */
+public class JOGLAppletLauncher extends Applet {
+ static {
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (Exception ignore) {
+ }
+ }
+
+ // metadata for native libraries
+ private static class NativeLibInfo {
+ private String osName;
+ private String osArch;
+ private String osNameAndArchPair;
+ private String nativePrefix;
+ private String nativeSuffix;
+
+ public NativeLibInfo(String osName, String osArch, String osNameAndArchPair, String nativePrefix, String nativeSuffix) {
+ this.osName = osName;
+ this.osArch = osArch;
+ this.osNameAndArchPair = osNameAndArchPair;
+ this.nativePrefix = nativePrefix;
+ this.nativeSuffix = nativeSuffix;
+ }
+
+ public boolean matchesOSAndArch(String osName, String osArch) {
+ if (osName.toLowerCase().startsWith(this.osName)) {
+ if ((this.osArch == null) ||
+ (osArch.toLowerCase().equals(this.osArch))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean matchesNativeLib(String fileName) {
+ if (fileName.toLowerCase().endsWith(nativeSuffix)) {
+ return true;
+ }
+ return false;
+ }
+
+ public String formatNativeJarName(String nativeJarPattern) {
+ return MessageFormat.format(nativeJarPattern, new Object[] { osNameAndArchPair });
+ }
+
+ public String getNativeLibName(String baseName) {
+ return nativePrefix + baseName + nativeSuffix;
+ }
+
+ public boolean isMacOS() {
+ return (osName.equals("mac"));
+ }
+
+ public boolean mayNeedDRIHack() {
+ return (!isMacOS() && !osName.equals("win"));
+ }
+ }
+
+ private static final NativeLibInfo[] allNativeLibInfo = {
+ new NativeLibInfo("win", "x86", "windows-i586", "", ".dll"),
+ new NativeLibInfo("win", "amd64", "windows-amd64", "", ".dll"),
+ new NativeLibInfo("win", "x86_64","windows-amd64", "", ".dll"),
+ new NativeLibInfo("mac", "ppc", "macosx-ppc", "lib", ".jnilib"),
+ new NativeLibInfo("mac", "i386", "macosx-universal", "lib", ".jnilib"),
+ new NativeLibInfo("linux", "i386", "linux-i586", "lib", ".so"),
+ new NativeLibInfo("linux", "x86", "linux-i586", "lib", ".so"),
+ new NativeLibInfo("linux", "amd64", "linux-amd64", "lib", ".so"),
+ new NativeLibInfo("linux", "x86_64","linux-amd64", "lib", ".so"),
+ new NativeLibInfo("sunos", "sparc", "solaris-sparc", "lib", ".so"),
+ new NativeLibInfo("sunos", "sparcv9","solaris-sparcv9", "lib", ".so"),
+ new NativeLibInfo("sunos", "x86", "solaris-i586", "lib", ".so"),
+ new NativeLibInfo("sunos", "amd64", "solaris-amd64", "lib", ".so"),
+ new NativeLibInfo("sunos", "x86_64","solaris-amd64", "lib", ".so")
+ };
+
+ private NativeLibInfo nativeLibInfo;
+ // Library names computed once the jar comes down.
+ // The signatures of these native libraries are checked before
+ // installing them.
+ private String[] nativeLibNames;
+
+ /** The applet we have to start */
+ private Applet subApplet;
+
+ private String subAppletClassName; // from applet PARAM
+ private String subAppletDisplayName; // from applet PARAM
+ /** URL string to an image used while installing */
+ private String subAppletImageName; // from applet PARAM
+
+ private String installDirectory; // (defines a private directory for native libs)
+
+ private JPanel loaderPanel = new JPanel(new BorderLayout());
+
+ private JProgressBar progressBar = new JProgressBar(0,100);
+
+ private boolean isInitOk = false;
+
+ /** false once start() has been invoked */
+ private boolean firstStart = true;
+
+ /** true if start() has passed successfully */
+ private boolean joglStarted = false;
+
+ /** Indicates whether JOAL is present */
+ private boolean haveJOAL = false;
+
+ // Helpers for question about whether to update deployment.properties
+ private static final String JRE_PREFIX = "deployment.javapi.jre.";
+ private static final String NODDRAW_PROP = "-Dsun.java2d.noddraw=true";
+ private static final String DONT_ASK = ".dont_ask";
+
+ public JOGLAppletLauncher() {
+ }
+
+ private static String md2Hash(String str) {
+ // Helps hash the jars in the "archive" tag into a hex value to
+ // avoid having too-long path names in the install directory's
+ // path name but also to have unique directories for each
+ // different archive set used (also meaning for each class loader
+ // loading something via the JOGLAppletLauncher) -- note that this
+ // is somewhat dependent on the Sun implementation of applets and
+ // their class loaders
+ MessageDigest md2 = null;
+ try {
+ md2 = MessageDigest.getInstance("MD2");
+ } catch (NoSuchAlgorithmException e) {
+ return "";
+ }
+ byte[] digest = md2.digest(str.getBytes());
+ if (digest == null || (digest.length == 0))
+ return "";
+ StringBuffer res = new StringBuffer();
+ for (int i = 0; i < digest.length; i++) {
+ res.append(Integer.toHexString(digest[i] & 0xFF));
+ }
+ return res.toString();
+ }
+
+ /** Applet initialization */
+ public void init() {
+
+ this.subAppletClassName = getParameter("subapplet.classname");
+ if (subAppletClassName == null){
+ displayError("Init failed : Missing subapplet.classname argument");
+ return;
+ }
+ this.subAppletDisplayName = getParameter("subapplet.displayname");
+ if (subAppletDisplayName == null){
+ subAppletDisplayName = "Applet";
+ }
+
+ this.subAppletImageName = getParameter("subapplet.image");
+
+ initLoaderLayout();
+ validate();
+
+ String extForm = getCodeBase().toExternalForm();
+ String codeBase = extForm.substring(extForm.indexOf(":") + 3); // minus http:// or https://
+
+ this.installDirectory = codeBase.replace(':', '_')
+ .replace('.', '_').replace('/', '_').replace('~','_') // clean up the name
+ + md2Hash(getParameter("archive")); // make it unique across different applet class loaders
+
+ String osName = System.getProperty("os.name");
+ String osArch = System.getProperty("os.arch");
+ if (checkOSAndArch(osName, osArch)) {
+ this.isInitOk = true;
+ } else {
+ displayError("Init failed : Unsupported os / arch ( " + osName + " / " + osArch + " )");
+ }
+ }
+
+ private void displayMessage(final String message){
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ progressBar.setString(message);
+ }
+ });
+ }
+
+ private void displayError(final String errorMessage){
+ // Print message to Java console too in case it's truncated in the applet's display
+ System.err.println(errorMessage);
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ progressBar.setString("Error : " + errorMessage);
+ }
+ });
+ }
+
+ private void setProgress(final int value) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ progressBar.setValue(value);
+ }
+ });
+ }
+
+ private void initLoaderLayout(){
+ setLayout(new BorderLayout());
+ progressBar.setBorderPainted(true);
+ progressBar.setStringPainted(true);
+ progressBar.setString("Loading...");
+ boolean includeImage = false;
+ ImageIcon image = null;
+ if (subAppletImageName != null){
+ try {
+ image = new ImageIcon(new URL(subAppletImageName));
+ includeImage = true;
+ } catch (MalformedURLException ex) {
+ ex.printStackTrace();
+ // not blocking
+ }
+ }
+ if (includeImage){
+ add(loaderPanel, BorderLayout.SOUTH);
+ loaderPanel.add(new JLabel(image), BorderLayout.CENTER);
+ loaderPanel.add(progressBar, BorderLayout.SOUTH);
+ } else {
+ add(loaderPanel, BorderLayout.SOUTH);
+ loaderPanel.add(progressBar, BorderLayout.CENTER);
+ }
+ }
+
+
+ /** start asynchroneous loading of libraries if needed */
+ public void start(){
+ if (isInitOk){
+ if (firstStart) {
+ firstStart = false;
+ String userHome = System.getProperty("user.home");
+
+ try {
+ // We need to load in the jogl package so that we can query the version information
+ ClassLoader classloader = getClass().getClassLoader();
+ classloader.loadClass("javax.media.opengl.GL");
+ Package p = Package.getPackage("javax.media.opengl");
+
+ String installDirName = userHome + File.separator + ".jogl_ext"
+ + File.separator + installDirectory + File.separator + p.getImplementationVersion().replace(':', '_');
+
+ final File installDir = new File(installDirName);
+
+ Thread refresher = new Thread() {
+ public void run() {
+ refreshJOGL(installDir);
+ }
+ };
+ refresher.setPriority(Thread.NORM_PRIORITY - 1);
+ refresher.start();
+ }
+ catch (ClassNotFoundException e) {
+ System.err.println("Unable to load javax.media.opengl package");
+ System.exit(0);
+ }
+
+ } else if (joglStarted) {
+ checkNoDDrawAndUpdateDeploymentProperties();
+ // we have to start again the applet (start can be called multiple times,
+ // e.g once per tabbed browsing
+ subApplet.start();
+ }
+ }
+ }
+
+ public void stop(){
+ if (subApplet != null){
+ subApplet.stop();
+ }
+ }
+
+ public void destroy(){
+ if (subApplet != null){
+ subApplet.destroy();
+ }
+ }
+
+
+ /** Helper method to make it easier to call methods on the
+ sub-applet from JavaScript. */
+ public Applet getSubApplet() {
+ return subApplet;
+ }
+
+ private boolean checkOSAndArch(String osName, String osArch) {
+ for (int i = 0; i < allNativeLibInfo.length; i++) {
+ NativeLibInfo info = allNativeLibInfo[i];
+ if (info.matchesOSAndArch(osName, osArch)) {
+ nativeLibInfo = info;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Get a "boolean" parameter, assuming that anything non-null aside
+ // from "false" is true
+ private boolean getBooleanParameter(String parameterName) {
+ String val = getParameter(parameterName);
+ if (val == null)
+ return false;
+ return !val.toLowerCase().equals("false");
+ }
+
+ private void checkNoDDrawAndUpdateDeploymentProperties() {
+ if (getBooleanParameter("jogl.disable.noddraw.check"))
+ return;
+ if (System.getProperty("os.name").toLowerCase().startsWith("windows") &&
+ !"true".equalsIgnoreCase(System.getProperty("sun.java2d.noddraw"))) {
+ if (!SwingUtilities.isEventDispatchThread()) {
+ try {
+ SwingUtilities.invokeAndWait(new Runnable() {
+ public void run() {
+ updateDeploymentPropertiesImpl();
+ }
+ });
+ } catch (Exception e) {
+ }
+ } else {
+ updateDeploymentPropertiesImpl();
+ }
+ }
+ }
+
+ private void updateDeploymentPropertiesImpl() {
+ String userHome = System.getProperty("user.home");
+ File dontAskFile = new File(userHome + File.separator + ".jogl_ext" +
+ File.separator + DONT_ASK);
+ if (dontAskFile.exists())
+ return; // User asked us not to prompt again
+
+ int option = 0;
+
+ if (!getBooleanParameter("jogl.silent.noddraw.check")) {
+ option = JOptionPane.showOptionDialog(null,
+ "For best robustness of JOGL applets on Windows,\n" +
+ "we recommend disabling Java2D's use of DirectDraw.\n" +
+ "This setting will affect all applets, but is unlikely\n" +
+ "to slow other applets down significantly. May we update\n" +
+ "your deployment.properties to turn off DirectDraw for\n" +
+ "applets? You can change this back later if necessary\n" +
+ "using the Java Control Panel, Java tab, under Java\n" +
+ "Applet Runtime Settings.",
+ "Update deployment.properties?",
+ JOptionPane.YES_NO_CANCEL_OPTION,
+ JOptionPane.QUESTION_MESSAGE,
+ null,
+ new Object[] {
+ "Yes",
+ "No",
+ "No, Don't Ask Again"
+ },
+ "Yes");
+ }
+
+ if (option < 0 ||
+ option == 1)
+ return; // No
+
+ if (option == 2) {
+ try {
+ dontAskFile.createNewFile();
+ } catch (IOException e) {
+ }
+ return; // No, Don't Ask Again
+ }
+
+ try {
+ // Must update deployment.properties
+ File propsDir = new File(System.getProperty("user.home") + File.separator +
+ "Application Data/Sun/Java/Deployment");
+ if (!propsDir.exists())
+ // Don't know what's going on or how to set this permanently
+ return;
+
+ File propsFile = new File(propsDir, "deployment.properties");
+ if (!propsFile.exists())
+ // Don't know what's going on or how to set this permanently
+ return;
+
+ Properties props = new Properties();
+ InputStream input = new BufferedInputStream(new FileInputStream(propsFile));
+ props.load(input);
+ input.close();
+ // Search through the keys looking for JRE versions
+ Set/*<String>*/ jreVersions = new HashSet/*<String>*/();
+ for (Iterator/*<String>*/ iter = props.keySet().iterator(); iter.hasNext(); ) {
+ String key = (String) iter.next();
+ if (key.startsWith(JRE_PREFIX)) {
+ int idx = key.lastIndexOf(".");
+ if (idx >= 0 && idx > JRE_PREFIX.length()) {
+ String jreVersion = key.substring(JRE_PREFIX.length(), idx);
+ jreVersions.add(jreVersion);
+ }
+ }
+ }
+
+ // Make sure the currently-running JRE shows up in this set to
+ // avoid repeated displays of the dialog. It might not in some
+ // upgrade scenarios where there was a pre-existing
+ // deployment.properties and the new Java Control Panel hasn't
+ // been run yet.
+ jreVersions.add(System.getProperty("java.version"));
+
+ // OK, now that we know all JRE versions covered by the
+ // deployment.properties, check out the args for each and update
+ // them
+ for (Iterator/*<String>*/ iter = jreVersions.iterator(); iter.hasNext(); ) {
+ String version = (String) iter.next();
+ String argKey = JRE_PREFIX + version + ".args";
+ String argVal = props.getProperty(argKey);
+ if (argVal == null) {
+ argVal = NODDRAW_PROP;
+ } else if (argVal.indexOf(NODDRAW_PROP) < 0) {
+ argVal = argVal + " " + NODDRAW_PROP;
+ }
+ props.setProperty(argKey, argVal);
+ }
+
+ OutputStream output = new BufferedOutputStream(new FileOutputStream(propsFile));
+ props.store(output, null);
+ output.close();
+
+ if (!getBooleanParameter("jogl.silent.noddraw.check")) {
+ // Tell user we're done
+ JOptionPane.showMessageDialog(null,
+ "For best robustness, we recommend you now exit and\n" +
+ "restart your web browser. (Note: clicking \"OK\" will\n" +
+ "not exit your browser.)",
+ "Browser Restart Recommended",
+ JOptionPane.INFORMATION_MESSAGE);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /** This method is executed from outside the Event Dispatch Thread, and installs
+ * the required native libraries in the local folder.
+ */
+ private void refreshJOGL(final File installDir) {
+ try {
+ Class subAppletClass = Class.forName(subAppletClassName);
+ // this will block until the applet jar is downloaded
+ } catch (ClassNotFoundException cnfe){
+ displayError("Start failed : class not found : " + subAppletClassName);
+ return;
+ }
+
+ if (!installDir.exists()){
+ if (!installDir.mkdirs()) {
+ displayError("Unable to create directories for target: " + installDir);
+ return;
+ }
+ }
+
+ // See whether JOAL is present
+ try {
+ Class alClass = Class.forName("net.java.games.joal.AL", false, this.getClass().getClassLoader());
+ haveJOAL = true;
+ // Note: it seems that some JRE implementations can throw
+ // SecurityException as well as ClassNotFoundException, at least
+ // if the OpenAL classes are not present and the web server
+ // redirects elsewhere
+ } catch (Exception e) {
+ }
+
+ String[] nativeJarNames = new String[] {
+ nativeLibInfo.formatNativeJarName("jogl-natives-{0}.jar"),
+ nativeLibInfo.formatNativeJarName("gluegen-rt-natives-{0}.jar"),
+ (haveJOAL ? nativeLibInfo.formatNativeJarName("joal-natives-{0}.jar") : null)
+ };
+
+ for (int n = 0; n < nativeJarNames.length; n++) {
+ String nativeJarName = nativeJarNames[n];
+
+ if (nativeJarName == null)
+ continue;
+
+ URL nativeLibURL;
+ URLConnection urlConnection;
+ String path = getCodeBase().toExternalForm() + nativeJarName;
+ try {
+ nativeLibURL = new URL(path);
+ urlConnection = nativeLibURL.openConnection();
+ } catch (Exception e){
+ e.printStackTrace();
+ displayError("Couldn't access the native lib URL : " + path);
+ return;
+ }
+
+ // the timestamp used to determine if we have to download the native jar again
+ // don't rely on the OS's timestamp to cache this
+ long lastModified = getTimestamp(installDir, nativeJarName, urlConnection.getLastModified());
+ if (lastModified != urlConnection.getLastModified()) {
+ displayMessage("Updating local version of the native libraries");
+ // first download the full jar locally
+ File localJarFile = new File(installDir, nativeJarName);
+ try {
+ saveNativesJarLocally(localJarFile, urlConnection);
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ displayError("Unable to install the native file locally");
+ return;
+ }
+
+ try {
+ JarFile jf = new JarFile(localJarFile);
+
+ // Iterate the entries finding all candidate libraries that need
+ // to have their signatures verified
+ if (!findNativeEntries(jf)) {
+ displayError("native libraries not found in jar file");
+ return;
+ }
+
+ byte[] buf = new byte[8192];
+
+ // Go back and verify the signatures
+ for (int i = 0; i < nativeLibNames.length; i++) {
+ JarEntry entry = jf.getJarEntry(nativeLibNames[i]);
+ if (entry == null) {
+ displayError("error looking up jar entry " + nativeLibNames[i]);
+ return;
+ }
+ if (!checkNativeCertificates(jf, entry, buf)) {
+ displayError("Native library " + nativeLibNames[i] + " isn't properly signed or has other errors");
+ return;
+ }
+ }
+
+ // Now install the native library files
+ setProgress(0);
+ for (int i = 0; i < nativeLibNames.length; i++) {
+ displayMessage("Installing native files from " + nativeJarName);
+ if (!installFile(installDir, jf, nativeLibNames[i], buf)) {
+ return;
+ }
+ int percent = (100 * (i + 1) / nativeLibNames.length);
+ setProgress(percent);
+ }
+
+ // At this point we can delete the jar file we just downloaded
+ jf.close();
+ localJarFile.delete();
+
+ // If installation succeeded, write a timestamp for all of the
+ // files to be checked next time
+ try {
+ File timestampFile = new File(installDir, getTimestampFileName(nativeJarName));
+ timestampFile.delete();
+ BufferedWriter writer = new BufferedWriter(new FileWriter(timestampFile));
+ writer.write("" + urlConnection.getLastModified());
+ writer.flush();
+ writer.close();
+ } catch (Exception e) {
+ displayError("Error writing time stamp for native libraries");
+ return;
+ }
+
+ } catch (Exception e) {
+ displayError("Error opening jar file " + localJarFile.getName() + " for reading");
+ return;
+ }
+ }
+ }
+
+ loadNativesAndStart(installDir);
+ }
+
+ private String getTimestampFileName(String nativeJarName) {
+ return "timestamp-" + nativeJarName.replace('.', '-');
+ }
+
+ private long getTimestamp(File installDir, String nativeJarName, long timestamp) {
+ // Avoid returning valid value if timestamp file doesn't exist
+ try {
+ String timestampName = getTimestampFileName(nativeJarName);
+ BufferedReader reader = new BufferedReader(new FileReader(new File(installDir, timestampName)));
+ try {
+ StreamTokenizer tokenizer = new StreamTokenizer(reader);
+ // Avoid screwing up by not being able to read full longs
+ tokenizer.resetSyntax();
+ tokenizer.wordChars('0', '9');
+ tokenizer.wordChars('-', '-');
+ tokenizer.nextToken();
+ String tok = tokenizer.sval;
+ if (tok != null) {
+ return Long.parseLong(tok);
+ }
+ } catch (Exception e) {
+ } finally {
+ reader.close();
+ }
+ } catch (Exception e) {
+ }
+ return ((timestamp == 0) ? 1 : 0);
+ }
+
+ private void saveNativesJarLocally(File localJarFile,
+ URLConnection urlConnection) throws IOException {
+ BufferedOutputStream out = null;;
+ InputStream in = null;
+ displayMessage("Downloading native library");
+ setProgress(0);
+ try {
+ out = new BufferedOutputStream(new
+ FileOutputStream(localJarFile));
+ int totalLength = urlConnection.getContentLength();
+ in = urlConnection.getInputStream();
+ byte[] buffer = new byte[1024];
+ int len;
+ int sum = 0;
+ while ( (len = in.read(buffer)) > 0) {
+ out.write(buffer, 0, len);
+ sum += len;
+ int percent = (100 * sum / totalLength);
+ setProgress(percent);
+ }
+ out.close();
+ in.close();
+ } finally {
+ // close the files
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException ignore) {
+ }
+ }
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException ignore) {
+ }
+ }
+ }
+ }
+
+ private boolean findNativeEntries(JarFile jf) {
+ List list = new ArrayList();
+ Enumeration e = jf.entries();
+ while (e.hasMoreElements()) {
+ JarEntry entry = (JarEntry) e.nextElement();
+ if (nativeLibInfo.matchesNativeLib(entry.getName())) {
+ list.add(entry.getName());
+ }
+ }
+ if (list.isEmpty()) {
+ return false;
+ }
+ nativeLibNames = (String[]) list.toArray(new String[0]);
+ return true;
+ }
+
+ /** checking the native certificates with the jogl ones (all must match)*/
+ private boolean checkNativeCertificates(JarFile jar, JarEntry entry, byte[] buf){
+ // API states that we must read all of the data from the entry's
+ // InputStream in order to be able to get its certificates
+ try {
+ InputStream is = jar.getInputStream(entry);
+ int totalLength = (int) entry.getSize();
+ int len;
+ while ((len = is.read(buf)) > 0) {
+ }
+ is.close();
+ Certificate[] nativeCerts = entry.getCertificates();
+ // locate the JOGL certificates
+ Certificate[] joglCerts = GLDrawableFactory.class.getProtectionDomain().
+ getCodeSource().getCertificates();
+
+ if (nativeCerts == null || nativeCerts.length == 0) {
+ return false;
+ }
+ int checked = 0;
+ for (int i = 0; i < joglCerts.length; i++) {
+ for (int j = 0; j < nativeCerts.length; j++) {
+ if (nativeCerts[j].equals(joglCerts[i])){
+ checked++;
+ break;
+ }
+ }
+ }
+ return (checked == joglCerts.length);
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ private boolean installFile(File installDir,
+ JarFile jar,
+ String fileName,
+ byte[] buf) {
+ try {
+ JarEntry entry = jar.getJarEntry(fileName);
+ if (entry == null) {
+ displayError("Error finding native library " + fileName);
+ return false;
+ }
+ InputStream is = jar.getInputStream(entry);
+ int totalLength = (int) entry.getSize();
+ BufferedOutputStream out = null;
+ File outputFile = new File(installDir, fileName);
+ boolean exists = false;
+ try {
+ exists = outputFile.exists();
+ out = new BufferedOutputStream(new FileOutputStream(outputFile));
+ } catch (Exception e) {
+ if (exists) {
+ // It's possible the files were updated on the web server
+ // but we still have them loaded in this process; skip this
+ // update
+ return true;
+ } else {
+ displayError("Error opening file " + fileName + " for writing");
+ return false;
+ }
+ }
+ int len;
+ try {
+ while ( (len = is.read(buf)) > 0) {
+ out.write(buf, 0, len);
+ }
+ } catch (IOException ioe) {
+ displayError("Error writing file " + fileName + " to disk");
+ ioe.printStackTrace();
+ outputFile.delete();
+ return false;
+ }
+ out.flush();
+ out.close();
+ is.close();
+ return true;
+ } catch (Exception e2) {
+ e2.printStackTrace();
+ displayError("Error writing file " + fileName + " to disk");
+ return false;
+ }
+ }
+
+ /** last step before launch : System.load() the natives and init()/start() the child applet */
+ private void loadNativesAndStart(final File nativeLibDir) {
+ // back to the EDT
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ displayMessage("Loading native libraries");
+
+ // disable JOGL and GlueGen runtime library loading from elsewhere
+ com.sun.opengl.impl.NativeLibLoader.disableLoading();
+ com.sun.gluegen.runtime.NativeLibLoader.disableLoading();
+
+ // Open GlueGen runtime library optimistically. Note that
+ // currently we do not need this on any platform except X11
+ // ones, because JOGL doesn't use the GlueGen NativeLibrary
+ // class anywhere except the DRIHack class, but if for
+ // example we add JOAL support then we will need this on
+ // every platform.
+ loadLibrary(nativeLibDir, "gluegen-rt");
+
+ Class driHackClass = null;
+ if (nativeLibInfo.mayNeedDRIHack()) {
+ // Run the DRI hack
+ try {
+ driHackClass = Class.forName("com.sun.opengl.impl.x11.DRIHack");
+ driHackClass.getMethod("begin", new Class[] {}).invoke(null, new Object[] {});
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ // Load core JOGL native library
+ loadLibrary(nativeLibDir, "jogl");
+
+ if (nativeLibInfo.mayNeedDRIHack()) {
+ // End DRI hack
+ try {
+ driHackClass.getMethod("end", new Class[] {}).invoke(null, new Object[] {});
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (!nativeLibInfo.isMacOS()) { // borrowed from NativeLibLoader
+ // Must pre-load JAWT on all non-Mac platforms to
+ // ensure references from jogl_awt shared object
+ // will succeed since JAWT shared object isn't in
+ // default library path
+ try {
+ System.loadLibrary("jawt");
+ } catch (UnsatisfiedLinkError ex) {
+ // Accessibility technologies load JAWT themselves; safe to continue
+ // as long as JAWT is loaded by any loader
+ if (ex.getMessage().indexOf("already loaded") == -1) {
+ displayError("Unable to load JAWT");
+ throw ex;
+ }
+ }
+ }
+
+ // Load AWT-specific native code
+ loadLibrary(nativeLibDir, "jogl_awt");
+
+ if (haveJOAL) {
+ // Turn off the System.loadLibrary call of the joal_native
+ // library. It will still need to load the OpenAL library
+ // internally via another mechanism.
+ try {
+ Class c = Class.forName("net.java.games.joal.impl.NativeLibLoader");
+ c.getMethod("disableLoading", new Class[] {}).invoke(null, new Object[] {});
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ // Append the installed native library directory to
+ // java.library.path. This is the most convenient way to
+ // make this directory available to the NativeLibrary code,
+ // which needs it for loading OpenAL if present.
+ String javaLibPath = System.getProperty("java.library.path");
+ String absPath = nativeLibDir.getAbsolutePath();
+ boolean shouldSet = false;
+ if (javaLibPath == null) {
+ javaLibPath = absPath;
+ shouldSet = true;
+ } else if (javaLibPath.indexOf(absPath) < 0) {
+ javaLibPath = javaLibPath + File.pathSeparator + absPath;
+ shouldSet = true;
+ }
+ if (shouldSet) {
+ System.setProperty("java.library.path", javaLibPath);
+ }
+
+ // Load core JOAL native library
+ loadLibrary(nativeLibDir, "joal_native");
+ }
+
+ displayMessage("Starting applet " + subAppletDisplayName);
+
+ // start the subapplet
+ startSubApplet();
+ }
+ });
+ }
+
+ private void loadLibrary(File installDir, String libName) {
+ String nativeLibName = nativeLibInfo.getNativeLibName(libName);
+ try {
+ System.load(new File(installDir, nativeLibName).getPath());
+ } catch (UnsatisfiedLinkError ex) {
+ // Note: if we have loaded this particular copy of the
+ // JOGL-related native library in another class loader, the
+ // steps taken above to ensure the installation directory name
+ // was unique have failed. We can't continue properly in this
+ // case, so just print and re-throw the exception.
+ ex.printStackTrace();
+ throw ex;
+ }
+ }
+
+ /** The true start of the sub applet (invoked in the EDT) */
+ private void startSubApplet(){
+ try {
+ subApplet = (Applet)Class.forName(subAppletClassName).newInstance();
+ subApplet.setStub(new AppletStubProxy());
+ } catch (ClassNotFoundException cnfe) {
+ cnfe.printStackTrace();
+ displayError("Class not found (" + subAppletClassName + ")");
+ return;
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ displayError("Unable to start " + subAppletDisplayName);
+ return;
+ }
+
+ add(subApplet, BorderLayout.CENTER);
+
+ try {
+ subApplet.init();
+ remove(loaderPanel);
+ validate();
+ checkNoDDrawAndUpdateDeploymentProperties();
+ subApplet.start();
+ joglStarted = true;
+ } catch (Exception ex){
+ ex.printStackTrace();
+ }
+
+ }
+
+ /** a proxy to allow the subApplet to work like a real applet */
+ class AppletStubProxy implements AppletStub {
+ public boolean isActive() {
+ return JOGLAppletLauncher.this.isActive();
+ }
+
+ public URL getDocumentBase() {
+ return JOGLAppletLauncher.this.getDocumentBase();
+ }
+
+ public URL getCodeBase() {
+ return JOGLAppletLauncher.this.getCodeBase();
+ }
+
+ public String getParameter(String name) {
+ return JOGLAppletLauncher.this.getParameter(name);
+ }
+
+ public AppletContext getAppletContext() {
+ return JOGLAppletLauncher.this.getAppletContext();
+ }
+
+ public void appletResize(int width, int height) {
+ JOGLAppletLauncher.this.resize(width, height);
+ }
+ }
+}
+