aboutsummaryrefslogtreecommitdiffstats
path: root/src/classes/javax/media/opengl/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/classes/javax/media/opengl/util')
-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
10 files changed, 3036 insertions, 0 deletions
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);
+ }
+ }
+}
+