From 32f216838432d0a67ce78061aa8a09261e3c3716 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Sat, 21 Jun 2008 02:52:27 +0000 Subject: *** empty log message *** git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/svn-server-sync/jogl/branches/JOGL_2_SANDBOX@1669 232f8b59-042b-4e1e-8c03-345bb8c30851 --- .../com/sun/opengl/util/texture/spi/DDSImage.java | 3 +- .../opengl/util/texture/spi/LEDataInputStream.java | 2 +- .../com/sun/opengl/util/texture/spi/SGIImage.java | 7 +- .../com/sun/opengl/util/texture/spi/TGAImage.java | 6 +- .../opengl/util/texture/spi/TextureProvider.java | 165 --- src/classes/javax/media/opengl/GLProfile.java | 148 +++ .../javax/media/opengl/j2d/gl2/GL2JPanel.java | 1319 ++++++++++++++++++++ src/classes/javax/media/opengl/util/Animator.java | 213 ++++ .../opengl/util/BufferUtil.java.javame_cdc_fp | 271 ++++ .../javax/media/opengl/util/BufferUtil.java.javase | 288 +++++ .../javax/media/opengl/util/FPSAnimator.java | 123 ++ src/classes/javax/media/opengl/util/Gamma.java | 106 ++ .../javax/media/opengl/util/ImmModeSink.java | 318 +++++ .../javax/media/opengl/util/VBOBufferDraw.java | 379 ++++++ .../opengl/util/gl2es1/VBOBufferDrawGL2ES1.java | 51 + .../javax/media/opengl/util/swing/JAnimator.java | 207 +++ .../opengl/util/swing/JOGLAppletLauncher.java | 1080 ++++++++++++++++ 17 files changed, 4513 insertions(+), 173 deletions(-) delete mode 100755 src/classes/com/sun/opengl/util/texture/spi/TextureProvider.java create mode 100644 src/classes/javax/media/opengl/GLProfile.java create mode 100644 src/classes/javax/media/opengl/j2d/gl2/GL2JPanel.java create mode 100755 src/classes/javax/media/opengl/util/Animator.java create mode 100755 src/classes/javax/media/opengl/util/BufferUtil.java.javame_cdc_fp create mode 100755 src/classes/javax/media/opengl/util/BufferUtil.java.javase create mode 100755 src/classes/javax/media/opengl/util/FPSAnimator.java create mode 100755 src/classes/javax/media/opengl/util/Gamma.java create mode 100644 src/classes/javax/media/opengl/util/ImmModeSink.java create mode 100644 src/classes/javax/media/opengl/util/VBOBufferDraw.java create mode 100644 src/classes/javax/media/opengl/util/gl2es1/VBOBufferDrawGL2ES1.java create mode 100755 src/classes/javax/media/opengl/util/swing/JAnimator.java create mode 100755 src/classes/javax/media/opengl/util/swing/JOGLAppletLauncher.java (limited to 'src/classes') diff --git a/src/classes/com/sun/opengl/util/texture/spi/DDSImage.java b/src/classes/com/sun/opengl/util/texture/spi/DDSImage.java index 835025b5c..01cdf4867 100755 --- a/src/classes/com/sun/opengl/util/texture/spi/DDSImage.java +++ b/src/classes/com/sun/opengl/util/texture/spi/DDSImage.java @@ -44,7 +44,8 @@ import java.nio.*; import java.nio.channels.*; import javax.media.opengl.*; -import com.sun.opengl.util.*; +import javax.media.opengl.util.*; +import com.sun.opengl.util.texture.*; /** A reader and writer for DirectDraw Surface (.dds) files, which are used to describe textures. These files can contain multiple mipmap diff --git a/src/classes/com/sun/opengl/util/texture/spi/LEDataInputStream.java b/src/classes/com/sun/opengl/util/texture/spi/LEDataInputStream.java index edbb6e35e..8baa1f414 100755 --- a/src/classes/com/sun/opengl/util/texture/spi/LEDataInputStream.java +++ b/src/classes/com/sun/opengl/util/texture/spi/LEDataInputStream.java @@ -68,7 +68,7 @@ import java.io.IOException; * @author Robin Luiten * @version 1.1 15/Dec/1997 */ -class LEDataInputStream extends FilterInputStream implements DataInput +public class LEDataInputStream extends FilterInputStream implements DataInput { /** * To reuse some of the non endian dependent methods from diff --git a/src/classes/com/sun/opengl/util/texture/spi/SGIImage.java b/src/classes/com/sun/opengl/util/texture/spi/SGIImage.java index 12523eb18..5360dc01d 100755 --- a/src/classes/com/sun/opengl/util/texture/spi/SGIImage.java +++ b/src/classes/com/sun/opengl/util/texture/spi/SGIImage.java @@ -43,10 +43,6 @@ import java.io.*; import javax.media.opengl.*; import com.sun.opengl.util.*; -// Test harness -import java.awt.image.*; -import javax.swing.*; - /**

Reads and writes SGI RGB/RGBA images.

Written from =0; i--) { + Class face = clazzes[i]; + if(face.getName().equals(faceName)) { + return true; + } + } + return false; + } + + public static final boolean implementationOfGL2(Object obj) { + return implementationOf(obj, "javax.media.opengl.GL2"); + } + + public static final boolean implementationOfGLES1(Object obj) { + return implementationOf(obj, "javax.media.opengl.GLES1"); + } + + public static final boolean implementationOfGLES2(Object obj) { + return implementationOf(obj, "javax.media.opengl.GLES2"); + } + + public static final boolean implementationOfGLES(Object obj) { + return implementationOfGLES1(obj) || implementationOfGLES2(obj); + } + + public static final boolean implementationOfGL2ES1(Object obj) { + return implementationOf(obj, "javax.media.opengl.GL2ES1"); + } + + public static final boolean implementationOfGL2ES2(Object obj) { + return implementationOf(obj, "javax.media.opengl.GL2ES2"); + } + +} + diff --git a/src/classes/javax/media/opengl/j2d/gl2/GL2JPanel.java b/src/classes/javax/media/opengl/j2d/gl2/GL2JPanel.java new file mode 100644 index 000000000..ca21c7777 --- /dev/null +++ b/src/classes/javax/media/opengl/j2d/gl2/GL2JPanel.java @@ -0,0 +1,1319 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package javax.media.opengl.j2d.gl2; + +import javax.media.opengl.*; +import javax.media.opengl.awt.*; + +import java.awt.*; +import java.awt.geom.*; +import java.awt.image.*; +import java.beans.*; +import javax.swing.*; +import java.nio.*; +import java.security.*; +import javax.swing.JComponent; +import javax.swing.JPanel; +import com.sun.opengl.impl.*; +import com.sun.opengl.impl.awt.*; +import com.sun.opengl.impl.j2d.*; + +// FIXME: Subclasses need to call resetGLFunctionAvailability() on their +// context whenever the displayChanged() function is called on their +// GLEventListeners + +/** A lightweight Swing component which provides OpenGL rendering + support. Provided for compatibility with Swing user interfaces + when adding a heavyweight doesn't work either because of + Z-ordering or LayoutManager problems.

+ + The GL2JPanel can be made transparent by creating it with a + GLCapabilities object with alpha bits specified and calling {@link + #setOpaque}(false). Pixels with resulting OpenGL alpha values less + than 1.0 will be overlaid on any underlying Swing rendering.

+ + Notes specific to the Reference Implementation: This component + attempts to use hardware-accelerated rendering via pbuffers and + falls back on to software rendering if problems occur. + Note that because this component attempts to use pbuffers for + rendering, and because pbuffers can not be resized, somewhat + surprising behavior may occur during resize operations; the {@link + GLEventListener#init} method may be called multiple times as the + pbuffer is resized to be able to cover the size of the GL2JPanel. + This behavior is correct, as the textures and display lists for + the GL2JPanel will have been lost during the resize operation. The + application should attempt to make its GLEventListener.init() + methods as side-effect-free as possible.

+ +*/ + +public class GL2JPanel extends JPanel implements AWTGLAutoDrawable { + private static final boolean DEBUG = Debug.debug("GL2JPanel"); + private static final boolean VERBOSE = Debug.verbose(); + + private GLDrawableHelper drawableHelper = new GLDrawableHelper(); + private volatile boolean isInitialized; + private volatile boolean shouldInitialize = false; + + // Data used for either pbuffers or pixmap-based offscreen surfaces + private GLCapabilities offscreenCaps; + private GLCapabilitiesChooser chooser; + private GLContext shareWith; + // This image is exactly the correct size to render into the panel + private BufferedImage offscreenImage; + // One of these is used to store the read back pixels before storing + // in the BufferedImage + private ByteBuffer readBackBytes; + private IntBuffer readBackInts; + private int readBackWidthInPixels; + private int readBackHeightInPixels; + // Width of the actual GL2JPanel + private int panelWidth = 0; + private int panelHeight = 0; + private Updater updater; + private int awtFormat; + private int glFormat; + private int glType; + // Lazy reshape notification + private boolean handleReshape = false; + private boolean sendReshape = true; + + private static GLDrawableFactoryImpl factory; + + // Implementation using pbuffers + private static boolean hardwareAccelerationDisabled = + Debug.isPropertyDefined("jogl.gljpanel.nohw"); + private static boolean softwareRenderingDisabled = + Debug.isPropertyDefined("jogl.gljpanel.nosw"); + private GLPbuffer pbuffer; + private int pbufferWidth = 256; + private int pbufferHeight = 256; + + // Implementation using software rendering + private GLDrawableImpl offscreenDrawable; + private GLContextImpl offscreenContext; + + // For handling reshape events lazily + private int reshapeX; + private int reshapeY; + private int reshapeWidth; + private int reshapeHeight; + + // For saving/restoring of OpenGL state during ReadPixels + private int[] swapbytes = new int[1]; + private int[] rowlength = new int[1]; + private int[] skiprows = new int[1]; + private int[] skippixels = new int[1]; + private int[] alignment = new int[1]; + + // Implementation using Java2D OpenGL pipeline's back buffer + private boolean oglPipelineEnabled = + Java2D.isOGLPipelineActive() && + !Debug.isPropertyDefined("jogl.gljpanel.noogl"); + // Opaque Object identifier representing the Java2D surface we are + // drawing to; used to determine when to destroy and recreate JOGL + // context + private Object j2dSurface; + // Graphics object being used during Java2D update action + // (absolutely essential to cache this) + private Graphics cached2DGraphics; + // No-op context representing the Java2D OpenGL context + private GLContext j2dContext; + // Context associated with no-op drawable representing the JOGL + // OpenGL context + private GLDrawable joglDrawable; + // The real OpenGL context JOGL uses to render + private GLContext joglContext; + // State captured from Java2D OpenGL context necessary in order to + // properly render into Java2D back buffer + private int[] drawBuffer = new int[1]; + private int[] readBuffer = new int[1]; + // This is required when the FBO option of the Java2D / OpenGL + // pipeline is active + private int[] frameBuffer = new int[1]; + // Current (as of this writing) NVidia drivers have a couple of bugs + // relating to the sharing of framebuffer and renderbuffer objects + // between contexts. It appears we have to (a) reattach the color + // attachment and (b) actually create new depth buffer storage and + // attach it in order for the FBO to behave properly in our context. + private boolean checkedForFBObjectWorkarounds; + private boolean fbObjectWorkarounds; + private int[] frameBufferDepthBuffer; + private int[] frameBufferTexture; + private boolean createNewDepthBuffer; + // Current (as of this writing) ATI drivers have problems when the + // same FBO is bound in two different contexts. Here we check for + // this case and explicitly release the FBO from Java2D's context + // before switching to ours. Java2D will re-bind the FBO when it + // makes its context current the next time. Interestingly, if we run + // this code path on NVidia hardware, it breaks the rendering + // results -- no output is generated. This doesn't appear to be an + // interaction with the abovementioned NVidia-specific workarounds, + // as even if we disable that code the FBO is still reported as + // incomplete in our context. + private boolean checkedGLVendor; + private boolean vendorIsATI; + + // Holding on to this GraphicsConfiguration is a workaround for a + // problem in the Java 2D / JOGL bridge when FBOs are enabled; see + // comment related to Issue 274 below + private GraphicsConfiguration workaroundConfig; + + // These are always set to (0, 0) except when the Java2D / OpenGL + // pipeline is active + private int viewportX; + private int viewportY; + + static { + // Force eager initialization of part of the Java2D class since + // otherwise it's likely it will try to be initialized while on + // the Queue Flusher Thread, which is not allowed + if (Java2D.isOGLPipelineActive() && Java2D.isFBOEnabled()) { + Java2D.getShareContext(GraphicsEnvironment. + getLocalGraphicsEnvironment(). + getDefaultScreenDevice(). + getDefaultConfiguration()); + } + GLProfile.setProfile(GLProfile.GL2); + factory = GLDrawableFactoryImpl.getFactoryImpl(true); + } + + /** Creates a new GL2JPanel component with a default set of OpenGL + capabilities and using the default OpenGL capabilities selection + mechanism. */ + public GL2JPanel() { + this(null); + } + + /** Creates a new GL2JPanel component with the requested set of + OpenGL capabilities, using the default OpenGL capabilities + selection mechanism. */ + public GL2JPanel(GLCapabilities capabilities) { + this(capabilities, null, null); + } + + /** Creates a new GL2JPanel component. The passed GLCapabilities + specifies the OpenGL capabilities for the component; if null, a + default set of capabilities is used. The GLCapabilitiesChooser + specifies the algorithm for selecting one of the available + GLCapabilities for the component; a DefaultGLCapabilitesChooser + is used if null is passed for this argument. The passed + GLContext specifies an OpenGL context with which to share + textures, display lists and other OpenGL state, and may be null + if sharing is not desired. See the note in the overview documentation on + context sharing. + */ + public GL2JPanel(GLCapabilities capabilities, GLCapabilitiesChooser chooser, GLContext shareWith) { + super(); + + // Works around problems on many vendors' cards; we don't need a + // back buffer for the offscreen surface anyway + if (capabilities != null) { + offscreenCaps = (GLCapabilities) capabilities.clone(); + } else { + offscreenCaps = new GLCapabilities(); + } + offscreenCaps.setDoubleBuffered(false); + this.chooser = ((chooser != null) ? chooser : new DefaultGLCapabilitiesChooser()); + this.shareWith = shareWith; + } + + public void display() { + if (EventQueue.isDispatchThread()) { + // Want display() to be synchronous, so call paintImmediately() + paintImmediately(0, 0, getWidth(), getHeight()); + } else { + // Multithreaded redrawing of Swing components is not allowed, + // so do everything on the event dispatch thread + try { + EventQueue.invokeAndWait(paintImmediatelyAction); + } catch (Exception e) { + throw new GLException(e); + } + } + } + + private void captureJ2DState(GL2 gl, Graphics g) { + gl.glGetIntegerv(GL2.GL_DRAW_BUFFER, drawBuffer, 0); + gl.glGetIntegerv(GL2.GL_READ_BUFFER, readBuffer, 0); + if (Java2D.isFBOEnabled() && + Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT) { + if (DEBUG && VERBOSE) { + System.err.println("GL2JPanel: Fetching GL_FRAMEBUFFER_BINDING_EXT"); + } + gl.glGetIntegerv(GL2.GL_FRAMEBUFFER_BINDING_EXT, frameBuffer, 0); + + if (fbObjectWorkarounds || + !checkedForFBObjectWorkarounds) { + // See above for description of what we are doing here + if (frameBufferTexture == null) + frameBufferTexture = new int[1]; + + // Query the framebuffer for its color buffer so we can hook + // it back up in our context (should not be necessary) + gl.glGetFramebufferAttachmentParameteriv(GL2.GL_FRAMEBUFFER_EXT, + GL2.GL_COLOR_ATTACHMENT0_EXT, + GL2.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT, + frameBufferTexture, 0); + if (DEBUG && VERBOSE) { + System.err.println("GL2JPanel: FBO COLOR_ATTACHMENT0: " + frameBufferTexture[0]); + } + } + + if (!checkedGLVendor) { + checkedGLVendor = true; + String vendor = gl.glGetString(GL2.GL_VENDOR); + + if ((vendor != null) && + vendor.startsWith("ATI")) { + vendorIsATI = true; + } + } + + if (vendorIsATI) { + // Unbind the FBO from Java2D's context as it appears that + // driver bugs on ATI's side are causing problems if the FBO is + // simultaneously bound to more than one context. Java2D will + // re-bind the FBO during the next validation of its context. + // Note: this breaks rendering at least on NVidia hardware + gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER_EXT, 0); + } + } + } + + private boolean preGL(Graphics g) { + GL2 gl = joglContext.getGL().getGL2(); + // Set up needed state in JOGL context from Java2D context + gl.glEnable(GL2.GL_SCISSOR_TEST); + Rectangle r = Java2D.getOGLScissorBox(g); + + if (r == null) { + if (DEBUG && VERBOSE) { + System.err.println("Java2D.getOGLScissorBox() returned null"); + } + return false; + } + if (DEBUG && VERBOSE) { + System.err.println("GL2JPanel: gl.glScissor(" + r.x + ", " + r.y + ", " + r.width + ", " + r.height + ")"); + } + + gl.glScissor(r.x, r.y, r.width, r.height); + Rectangle oglViewport = Java2D.getOGLViewport(g, panelWidth, panelHeight); + // If the viewport X or Y changes, in addition to the panel's + // width or height, we need to send a reshape operation to the + // client + if ((viewportX != oglViewport.x) || + (viewportY != oglViewport.y)) { + sendReshape = true; + if (DEBUG) { + System.err.println("Sending reshape because viewport changed"); + System.err.println(" viewportX (" + viewportX + ") ?= oglViewport.x (" + oglViewport.x + ")"); + System.err.println(" viewportY (" + viewportY + ") ?= oglViewport.y (" + oglViewport.y + ")"); + } + } + viewportX = oglViewport.x; + viewportY = oglViewport.y; + + // If the FBO option is active, bind to the FBO from the Java2D + // context. + // Note that all of the plumbing in the context sharing stuff will + // allow us to bind to this object since it's in our namespace. + if (Java2D.isFBOEnabled() && + Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT) { + if (DEBUG && VERBOSE) { + System.err.println("GL2JPanel: Binding to framebuffer object " + frameBuffer[0]); + } + + // The texture target for Java2D's OpenGL pipeline when using FBOs + // -- either GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE_ARB + int fboTextureTarget = Java2D.getOGLTextureType(g); + + if (!checkedForFBObjectWorkarounds) { + checkedForFBObjectWorkarounds = true; + gl.glBindTexture(fboTextureTarget, 0); + gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER_EXT, frameBuffer[0]); + if (gl.glCheckFramebufferStatus(GL2.GL_FRAMEBUFFER_EXT) != + GL2.GL_FRAMEBUFFER_COMPLETE_EXT) { + // Need to do workarounds + fbObjectWorkarounds = true; + createNewDepthBuffer = true; + if (DEBUG) { + System.err.println("-- GL2JPanel: discovered frame_buffer_object workarounds to be necessary"); + } + } else { + // Don't need the frameBufferTexture temporary any more + frameBufferTexture = null; + } + } + + if (fbObjectWorkarounds && createNewDepthBuffer) { + if (frameBufferDepthBuffer == null) + frameBufferDepthBuffer = new int[1]; + + // Create our own depth renderbuffer and associated storage + // If we have an old one, delete it + if (frameBufferDepthBuffer[0] != 0) { + gl.glDeleteRenderbuffers(1, frameBufferDepthBuffer, 0); + frameBufferDepthBuffer[0] = 0; + } + + gl.glBindTexture(fboTextureTarget, frameBufferTexture[0]); + int[] width = new int[1]; + int[] height = new int[1]; + gl.glGetTexLevelParameteriv(fboTextureTarget, 0, GL2.GL_TEXTURE_WIDTH, width, 0); + gl.glGetTexLevelParameteriv(fboTextureTarget, 0, GL2.GL_TEXTURE_HEIGHT, height, 0); + + gl.glGenRenderbuffers(1, frameBufferDepthBuffer, 0); + if (DEBUG) { + System.err.println("GL2JPanel: Generated frameBufferDepthBuffer " + frameBufferDepthBuffer[0] + + " with width " + width[0] + ", height " + height[0]); + } + + gl.glBindRenderbuffer(GL2.GL_RENDERBUFFER_EXT, frameBufferDepthBuffer[0]); + // FIXME: may need a loop here like in Java2D + gl.glRenderbufferStorage(GL2.GL_RENDERBUFFER_EXT, GL2.GL_DEPTH_COMPONENT24, width[0], height[0]); + + gl.glBindRenderbuffer(GL2.GL_RENDERBUFFER_EXT, 0); + createNewDepthBuffer = false; + } + + gl.glBindTexture(fboTextureTarget, 0); + gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER_EXT, frameBuffer[0]); + + if (fbObjectWorkarounds) { + // Hook up the color and depth buffer attachment points for this framebuffer + gl.glFramebufferTexture2D(GL2.GL_FRAMEBUFFER_EXT, + GL2.GL_COLOR_ATTACHMENT0_EXT, + fboTextureTarget, + frameBufferTexture[0], + 0); + if (DEBUG && VERBOSE) { + System.err.println("GL2JPanel: frameBufferDepthBuffer: " + frameBufferDepthBuffer[0]); + } + gl.glFramebufferRenderbuffer(GL2.GL_FRAMEBUFFER_EXT, + GL2.GL_DEPTH_ATTACHMENT_EXT, + GL2.GL_RENDERBUFFER_EXT, + frameBufferDepthBuffer[0]); + } + + if (DEBUG) { + int status = gl.glCheckFramebufferStatus(GL2.GL_FRAMEBUFFER_EXT); + if (status != GL2.GL_FRAMEBUFFER_COMPLETE_EXT) { + throw new GLException("Error: framebuffer was incomplete: status = 0x" + + Integer.toHexString(status)); + } + } + } else { + if (DEBUG && VERBOSE) { + System.err.println("GL2JPanel: Setting up drawBuffer " + drawBuffer[0] + + " and readBuffer " + readBuffer[0]); + } + + gl.glDrawBuffer(drawBuffer[0]); + gl.glReadBuffer(readBuffer[0]); + } + + return true; + } + + private void postGL(Graphics g) { + if (Java2D.isFBOEnabled() && + Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT) { + // Unbind the framebuffer from our context to work around + // apparent driver bugs or at least unspecified behavior causing + // OpenGL to run out of memory with certain cards and drivers + GL2 gl = joglContext.getGL().getGL2(); + gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER_EXT, 0); + } + } + + /** Overridden to cause OpenGL rendering to be performed during + repaint cycles. Subclasses which override this method must call + super.paintComponent() in their paintComponent() method in order + to function properly.

+ + Overrides: +

paintComponent in class javax.swing.JComponent
*/ + protected void paintComponent(final Graphics g) { + if (Beans.isDesignTime()) { + // Make GL2JPanel behave better in NetBeans GUI builder + g.setColor(Color.BLACK); + g.fillRect(0, 0, getWidth(), getHeight()); + FontMetrics fm = g.getFontMetrics(); + String name = getName(); + if (name == null) { + name = getClass().getName(); + int idx = name.lastIndexOf('.'); + if (idx >= 0) { + name = name.substring(idx + 1); + } + } + Rectangle2D bounds = fm.getStringBounds(name, g); + g.setColor(Color.WHITE); + g.drawString(name, + (int) ((getWidth() - bounds.getWidth()) / 2), + (int) ((getHeight() + bounds.getHeight()) / 2)); + return; + } + + if (shouldInitialize) { + initialize(); + } + + if (!isInitialized) { + return; + } + + // NOTE: must do this when the context is not current as it may + // involve destroying the pbuffer (current context) and + // re-creating it -- tricky to do properly while the context is + // current + if (handleReshape) { + handleReshape(); + } + + updater.setGraphics(g); + + if (oglPipelineEnabled) { + + // This is a workaround for an issue in the Java 2D / JOGL + // bridge (reported by an end user as JOGL Issue 274) where Java + // 2D can occasionally leave its internal OpenGL context current + // to the on-screen window rather than its internal "scratch" + // pbuffer surface to which the FBO is attached. JOGL expects to + // find a stable OpenGL drawable (on Windows, an HDC) upon which + // it can create another OpenGL context. It turns out that, on + // Windows, when Java 2D makes its internal OpenGL context + // current against the window in order to put pixels on the + // screen, it gets the device context for the window, makes its + // context current, and releases the device context. This means + // that when JOGL's Runnable gets to run below, the HDC is + // already invalid. The workaround for this is to force Java 2D + // to make its context current to the scratch surface, which we + // can do by executing an empty Runnable with the "shared" + // context current. This will be fixed in a Java SE 6 update + // release, hopefully 6u2. + if (Java2D.isFBOEnabled()) { + if (workaroundConfig == null) { + workaroundConfig = GraphicsEnvironment. + getLocalGraphicsEnvironment(). + getDefaultScreenDevice(). + getDefaultConfiguration(); + } + Java2D.invokeWithOGLSharedContextCurrent(workaroundConfig, new Runnable() { public void run() {}}); + } + + Java2D.invokeWithOGLContextCurrent(g, new Runnable() { + public void run() { + if (DEBUG && VERBOSE) { + System.err.println("-- In invokeWithOGLContextCurrent"); + } + + // Create no-op context representing Java2D context + if (j2dContext == null) { + j2dContext = factory.createExternalGLContext(); + if (DEBUG) { + j2dContext.setGL(new DebugGL2(j2dContext.getGL().getGL2())); + } + + // Check to see whether we can support the requested + // capabilities or need to fall back to a pbuffer + // FIXME: add more checks? + + j2dContext.makeCurrent(); + GL2 gl = j2dContext.getGL().getGL2(); + if ((getGLInteger(gl, GL2.GL_RED_BITS) < offscreenCaps.getRedBits()) || + (getGLInteger(gl, GL2.GL_GREEN_BITS) < offscreenCaps.getGreenBits()) || + (getGLInteger(gl, GL2.GL_BLUE_BITS) < offscreenCaps.getBlueBits()) || + // (getGLInteger(gl, GL2.GL_ALPHA_BITS) < offscreenCaps.getAlphaBits()) || + (getGLInteger(gl, GL2.GL_ACCUM_RED_BITS) < offscreenCaps.getAccumRedBits()) || + (getGLInteger(gl, GL2.GL_ACCUM_GREEN_BITS) < offscreenCaps.getAccumGreenBits()) || + (getGLInteger(gl, GL2.GL_ACCUM_BLUE_BITS) < offscreenCaps.getAccumBlueBits()) || + (getGLInteger(gl, GL2.GL_ACCUM_ALPHA_BITS) < offscreenCaps.getAccumAlphaBits()) || + // (getGLInteger(gl, GL2.GL_DEPTH_BITS) < offscreenCaps.getDepthBits()) || + (getGLInteger(gl, GL2.GL_STENCIL_BITS) < offscreenCaps.getStencilBits())) { + if (DEBUG) { + System.err.println("GL2JPanel: Falling back to pbuffer-based support because Java2D context insufficient"); + System.err.println(" Available Required"); + System.err.println("GL_RED_BITS " + getGLInteger(gl, GL2.GL_RED_BITS) + " " + offscreenCaps.getRedBits()); + System.err.println("GL_GREEN_BITS " + getGLInteger(gl, GL2.GL_GREEN_BITS) + " " + offscreenCaps.getGreenBits()); + System.err.println("GL_BLUE_BITS " + getGLInteger(gl, GL2.GL_BLUE_BITS) + " " + offscreenCaps.getBlueBits()); + System.err.println("GL_ALPHA_BITS " + getGLInteger(gl, GL2.GL_ALPHA_BITS) + " " + offscreenCaps.getAlphaBits()); + System.err.println("GL_ACCUM_RED_BITS " + getGLInteger(gl, GL2.GL_ACCUM_RED_BITS) + " " + offscreenCaps.getAccumRedBits()); + System.err.println("GL_ACCUM_GREEN_BITS " + getGLInteger(gl, GL2.GL_ACCUM_GREEN_BITS) + " " + offscreenCaps.getAccumGreenBits()); + System.err.println("GL_ACCUM_BLUE_BITS " + getGLInteger(gl, GL2.GL_ACCUM_BLUE_BITS) + " " + offscreenCaps.getAccumBlueBits()); + System.err.println("GL_ACCUM_ALPHA_BITS " + getGLInteger(gl, GL2.GL_ACCUM_ALPHA_BITS) + " " + offscreenCaps.getAccumAlphaBits()); + System.err.println("GL_DEPTH_BITS " + getGLInteger(gl, GL2.GL_DEPTH_BITS) + " " + offscreenCaps.getDepthBits()); + System.err.println("GL_STENCIL_BITS " + getGLInteger(gl, GL2.GL_STENCIL_BITS) + " " + offscreenCaps.getStencilBits()); + } + isInitialized = false; + shouldInitialize = true; + oglPipelineEnabled = false; + handleReshape = true; + j2dContext.release(); + j2dContext.destroy(); + j2dContext = null; + return; + } + j2dContext.release(); + } + + j2dContext.makeCurrent(); + try { + captureJ2DState(j2dContext.getGL().getGL2(), g); + Object curSurface = Java2D.getOGLSurfaceIdentifier(g); + if (curSurface != null) { + if (j2dSurface != curSurface) { + if (joglContext != null) { + joglContext.destroy(); + joglContext = null; + joglDrawable = null; + sendReshape = true; + if (DEBUG) { + System.err.println("Sending reshape because surface changed"); + System.err.println("New surface = " + curSurface); + } + } + j2dSurface = curSurface; + } + if (joglContext == null) { + if (factory.canCreateExternalGLDrawable()) { + joglDrawable = factory.createExternalGLDrawable(); + joglContext = joglDrawable.createContext(shareWith); + } else if (factory.canCreateContextOnJava2DSurface()) { + // Mac OS X code path + joglContext = factory.createContextOnJava2DSurface(g, shareWith); + } + if (DEBUG) { + joglContext.setGL(new DebugGL2(joglContext.getGL().getGL2())); + } + + if (Java2D.isFBOEnabled() && + Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT && + fbObjectWorkarounds) { + createNewDepthBuffer = true; + } + } + if (joglContext instanceof Java2DGLContext) { + // Mac OS X code path + ((Java2DGLContext) joglContext).setGraphics(g); + } + + if (DEBUG && VERBOSE && Java2D.isFBOEnabled()) { + System.err.print("-- Surface type: "); + int surfaceType = Java2D.getOGLSurfaceType(g); + if (surfaceType == Java2D.UNDEFINED) { + System.err.println("UNDEFINED"); + } else if (surfaceType == Java2D.WINDOW) { + System.err.println("WINDOW"); + } else if (surfaceType == Java2D.PBUFFER) { + System.err.println("PBUFFER"); + } else if (surfaceType == Java2D.TEXTURE) { + System.err.println("TEXTURE"); + } else if (surfaceType == Java2D.FLIP_BACKBUFFER) { + System.err.println("FLIP_BACKBUFFER"); + } else if (surfaceType == Java2D.FBOBJECT) { + System.err.println("FBOBJECT"); + } else { + System.err.println("(Unknown surface type " + surfaceType + ")"); + } + } + + drawableHelper.invokeGL(joglDrawable, joglContext, displayAction, initAction); + } + } finally { + j2dContext.release(); + } + } + }); + } else { + if (!hardwareAccelerationDisabled) { + pbuffer.display(); + } else { + drawableHelper.invokeGL(offscreenDrawable, offscreenContext, displayAction, initAction); + } + + if (offscreenImage != null) { + // Draw resulting image in one shot + g.drawImage(offscreenImage, 0, 0, offscreenImage.getWidth(), offscreenImage.getHeight(), this); + } + } + } + + /** Overridden to track when this component is added to a container. + Subclasses which override this method must call + super.addNotify() in their addNotify() method in order to + function properly.

+ + Overrides: +

addNotify in class java.awt.Component
*/ + public void addNotify() { + super.addNotify(); + shouldInitialize = true; + if (DEBUG) { + System.err.println("GL2JPanel.addNotify()"); + } + } + + /** Overridden to track when this component is removed from a + container. Subclasses which override this method must call + super.removeNotify() in their removeNotify() method in order to + function properly.

+ + Overrides: +

removeNotify in class java.awt.Component
*/ + public void removeNotify() { + if (DEBUG) { + System.err.println("GL2JPanel.removeNotify()"); + } + if (oglPipelineEnabled) { + Java2D.invokeWithOGLContextCurrent(null, new Runnable() { + public void run() { + if (joglContext != null) { + joglContext.destroy(); + joglContext = null; + } + joglDrawable = null; + if (j2dContext != null) { + j2dContext.destroy(); + j2dContext = null; + } + } + }); + } else { + if (!hardwareAccelerationDisabled) { + if (pbuffer != null) { + pbuffer.destroy(); + pbuffer = null; + } + } else { + if (offscreenContext != null) { + offscreenContext.destroy(); + offscreenContext = null; + } + if (offscreenDrawable != null) { + offscreenDrawable.destroy(); + offscreenDrawable = null; + } + } + } + isInitialized = false; + super.removeNotify(); + } + + /** Overridden to cause {@link GLDrawableHelper#reshape} to be + called on all registered {@link GLEventListener}s. Subclasses + which override this method must call super.reshape() in + their reshape() method in order to function properly.

+ + Overrides: +

reshape in class java.awt.Component
*/ + public void reshape(int x, int y, int width, int height) { + super.reshape(x, y, width, height); + + reshapeX = x; + reshapeY = y; + reshapeWidth = width; + reshapeHeight = height; + handleReshape = true; + } + + public void setOpaque(boolean opaque) { + if (opaque != isOpaque()) { + if (offscreenImage != null) { + offscreenImage.flush(); + offscreenImage = null; + } + } + super.setOpaque(opaque); + } + + public void addGLEventListener(GLEventListener listener) { + drawableHelper.addGLEventListener(listener); + } + + public void removeGLEventListener(GLEventListener listener) { + drawableHelper.removeGLEventListener(listener); + } + + public GLContext createContext(GLContext shareWith) { + if (!hardwareAccelerationDisabled) { + return pbuffer.createContext(shareWith); + } else { + return offscreenDrawable.createContext(shareWith); + } + } + + public void setRealized(boolean realized) { + } + + public GLContext getContext() { + if (oglPipelineEnabled) { + return joglContext; + } else { + if (!hardwareAccelerationDisabled) { + // Workaround for crashes in NetBeans GUI builder + if (pbuffer == null && Beans.isDesignTime()) { + return null; + } + return pbuffer.getContext(); + } else { + return offscreenContext; + } + } + } + + public GL getGL() { + GLContext context = getContext(); + return (context == null) ? null : context.getGL(); + } + + public void setGL(GL gl) { + if(!gl.isGL2()) { + throw new GLException("not a GL2 implementation"); + } + GLContext context = getContext(); + if (context != null) { + context.setGL(gl); + } + } + + public void setAutoSwapBufferMode(boolean onOrOff) { + if (!hardwareAccelerationDisabled) { + // Workaround for crashes in NetBeans GUI builder + if (pbuffer == null && Beans.isDesignTime()) { + return; + } + pbuffer.setAutoSwapBufferMode(onOrOff); + } else { + drawableHelper.setAutoSwapBufferMode(onOrOff); + } + } + + public boolean getAutoSwapBufferMode() { + if (!hardwareAccelerationDisabled && !oglPipelineEnabled) { + return pbuffer.getAutoSwapBufferMode(); + } else { + return drawableHelper.getAutoSwapBufferMode(); + } + } + + public void swapBuffers() { + // In the current implementation this is basically a no-op. Both + // the pbuffer and pixmap based rendering paths use a single- + // buffered surface so swapping the buffers doesn't do anything. + // We also don't currently have the provision to skip copying the + // data to the Swing portion of the GL2JPanel in any of the + // rendering paths. + if (oglPipelineEnabled) { + // Do nothing + } else if (!hardwareAccelerationDisabled) { + pbuffer.swapBuffers(); + } else { + drawableHelper.invokeGL(offscreenDrawable, offscreenContext, swapBuffersAction, initAction); + } + } + + /** For a translucent GL2JPanel (one for which {@link #setOpaque + setOpaque}(false) has been called), indicates whether the + application should preserve the OpenGL color buffer + (GL_COLOR_BUFFER_BIT) for correct rendering of the GL2JPanel and + underlying widgets which may show through portions of the + GL2JPanel with alpha values less than 1. Most Swing + implementations currently expect the GL2JPanel to be completely + cleared (e.g., by glClear(GL_COLOR_BUFFER_BIT | + GL_DEPTH_BUFFER_BIT)), but for certain optimized Swing + implementations which use OpenGL internally, it may be possible + to perform OpenGL rendering using the GL2JPanel into the same + OpenGL drawable as the Swing implementation uses. */ + public boolean shouldPreserveColorBufferIfTranslucent() { + return oglPipelineEnabled; + } + + protected GLCapabilities caps=null; + + public GLCapabilities getChosenGLCapabilities() { + if (oglPipelineEnabled) { + if(caps==null) { + caps = new GLCapabilities(); + } + return caps; + } + + if (hardwareAccelerationDisabled) { + if (offscreenDrawable != null) + return offscreenDrawable.getChosenGLCapabilities(); + } else { + if (pbuffer != null) + return pbuffer.getChosenGLCapabilities(); + } + + return null; + } + + public void setChosenGLCapabilities(GLCapabilities caps) { + if (oglPipelineEnabled) { + this.caps = (caps==null) ? null : (GLCapabilities) caps.clone(); + } + + if (hardwareAccelerationDisabled) { + if (offscreenDrawable != null) + offscreenDrawable.setChosenGLCapabilities(caps); + } else { + if (pbuffer != null) + pbuffer.setChosenGLCapabilities(caps); + } + } + + public NativeWindow getNativeWindow() { + throw new GLException("FIXME"); + } + + public GLDrawableFactory getFactory() { + return factory; + } + + public int lockSurface() throws GLException { + throw new GLException("FIXME"); + } + public void unlockSurface() { + throw new GLException("FIXME"); + } + public boolean isSurfaceLocked() { + throw new GLException("FIXME"); + } + public void destroy() { + throw new GLException("FIXME"); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private void initialize() { + if (panelWidth == 0 || + panelHeight == 0) { + // See whether we have a non-zero size yet and can go ahead with + // initialization + if (reshapeWidth == 0 || + reshapeHeight == 0) { + return; + } + + // Pull down reshapeWidth and reshapeHeight into panelWidth and + // panelHeight eagerly in order to complete initialization, and + // force a reshape later + panelWidth = reshapeWidth; + panelHeight = reshapeHeight; + } + + if (!oglPipelineEnabled) { + // Initialize either the hardware-accelerated rendering path or + // the lightweight rendering path + if (!hardwareAccelerationDisabled) { + if (factory.canCreateGLPbuffer()) { + if (pbuffer != null) { + throw new InternalError("Creating pbuffer twice without destroying it (memory leak / correctness bug)"); + } + try { + pbuffer = factory.createGLPbuffer(offscreenCaps, + null, + pbufferWidth, + pbufferHeight, + shareWith); + updater = new Updater(); + pbuffer.addGLEventListener(updater); + shouldInitialize = false; + isInitialized = true; + return; + } catch (GLException e) { + if (DEBUG) { + e.printStackTrace(); + System.err.println("GL2JPanel: Falling back on software rendering because of problems creating pbuffer"); + } + hardwareAccelerationDisabled = true; + } + } else { + if (DEBUG) { + System.err.println("GL2JPanel: Falling back on software rendering because no pbuffer support"); + } + + // If the factory reports that it can't create a pbuffer, + // don't try again the next time, and fall through to the + // software rendering path + hardwareAccelerationDisabled = true; + } + } + + if (softwareRenderingDisabled) { + throw new GLException("Fallback to software rendering disabled by user"); + } + + // Fall-through path: create an offscreen context instead + offscreenDrawable = factory.createOffscreenDrawable(offscreenCaps, chooser); + offscreenDrawable.setSize(Math.max(1, panelWidth), Math.max(1, panelHeight)); + offscreenContext = (GLContextImpl) offscreenDrawable.createContext(shareWith); + offscreenContext.setSynchronized(true); + } + updater = new Updater(); + shouldInitialize = false; + isInitialized = true; + } + + private void handleReshape() { + readBackWidthInPixels = 0; + readBackHeightInPixels = 0; + + panelWidth = reshapeWidth; + panelHeight = reshapeHeight; + + if (DEBUG) { + System.err.println("GL2JPanel.handleReshape: (w,h) = (" + + panelWidth + "," + panelHeight + ")"); + } + + sendReshape = true; + + if (!oglPipelineEnabled) { + if (!hardwareAccelerationDisabled) { + // Use factor larger than 2 during shrinks for some hysteresis + float shrinkFactor = 2.5f; + if ((panelWidth > pbufferWidth ) || (panelHeight > pbufferHeight) || + (panelWidth < (pbufferWidth / shrinkFactor)) || (panelHeight < (pbufferHeight / shrinkFactor))) { + if (DEBUG) { + System.err.println("Resizing pbuffer from (" + pbufferWidth + ", " + pbufferHeight + ") " + + " to fit (" + panelWidth + ", " + panelHeight + ")"); + } + // Must destroy and recreate pbuffer to fit + if (pbuffer != null) { + // Watch for errors during pbuffer destruction (due to + // buggy / bad OpenGL drivers, in particular SiS) and fall + // back to software rendering + try { + pbuffer.destroy(); + } catch (GLException e) { + hardwareAccelerationDisabled = true; + if (DEBUG) { + System.err.println("WARNING: falling back to software rendering due to bugs in OpenGL drivers"); + e.printStackTrace(); + } + } + } + pbuffer = null; + isInitialized = false; + pbufferWidth = getNextPowerOf2(panelWidth); + pbufferHeight = getNextPowerOf2(panelHeight); + if (DEBUG && !hardwareAccelerationDisabled) { + System.err.println("New pbuffer size is (" + pbufferWidth + ", " + pbufferHeight + ")"); + } + initialize(); + } + + // It looks like NVidia's drivers (at least the ones on my + // notebook) are buggy and don't allow a rectangle of less than + // the pbuffer's width to be read...this doesn't really matter + // because it's the Graphics.drawImage() calls that are the + // bottleneck. Should probably make the size of the offscreen + // image be the exact size of the pbuffer to save some work on + // resize operations... + if (!hardwareAccelerationDisabled) { + readBackWidthInPixels = pbufferWidth; + readBackHeightInPixels = panelHeight; + } else { + // Just disabled hardware acceleration during this resize operation; do a fixup + readBackWidthInPixels = Math.max(1, panelWidth); + readBackHeightInPixels = Math.max(1, panelHeight); + } + } else { + offscreenContext.destroy(); + offscreenDrawable.setSize(Math.max(1, panelWidth), Math.max(1, panelHeight)); + readBackWidthInPixels = Math.max(1, panelWidth); + readBackHeightInPixels = Math.max(1, panelHeight); + } + + if (offscreenImage != null) { + offscreenImage.flush(); + offscreenImage = null; + } + } + + handleReshape = false; + } + + // FIXME: it isn't clear whether this works any more given that + // we're accessing the GLDrawable inside of the GLPbuffer directly + // up in reshape() -- need to rethink and clean this up + class Updater implements GLEventListener { + private Graphics g; + + public void setGraphics(Graphics g) { + this.g = g; + } + + public void init(GLAutoDrawable drawable) { + if (oglPipelineEnabled) { + if (!preGL(g)) { + return; + } + } + drawableHelper.init(GL2JPanel.this); + if (oglPipelineEnabled) { + postGL(g); + } + } + + public void display(GLAutoDrawable drawable) { + if (oglPipelineEnabled) { + if (!preGL(g)) { + return; + } + } + + if (sendReshape) { + if (DEBUG) { + System.err.println("glViewport(" + viewportX + ", " + viewportY + ", " + panelWidth + ", " + panelHeight + ")"); + } + getGL().getGL2().glViewport(viewportX, viewportY, panelWidth, panelHeight); + drawableHelper.reshape(GL2JPanel.this, viewportX, viewportY, panelWidth, panelHeight); + sendReshape = false; + } + + drawableHelper.display(GL2JPanel.this); + + if (!oglPipelineEnabled) { + // Must now copy pixels from offscreen context into surface + if (offscreenImage == null) { + if (panelWidth > 0 && panelHeight > 0) { + // It looks like NVidia's drivers (at least the ones on my + // notebook) are buggy and don't allow a sub-rectangle to be + // read from a pbuffer...this doesn't really matter because + // it's the Graphics.drawImage() calls that are the + // bottleneck + + int awtFormat = 0; + int hwGLFormat = 0; + if (!hardwareAccelerationDisabled) { + // This seems to be a good choice on all platforms + hwGLFormat = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + } + + // Should be more flexible in these BufferedImage formats; + // perhaps see what the preferred image types are on the + // given platform + if (isOpaque()) { + awtFormat = BufferedImage.TYPE_INT_RGB; + } else { + awtFormat = BufferedImage.TYPE_INT_ARGB; + } + + offscreenImage = new BufferedImage(panelWidth, + panelHeight, + awtFormat); + switch (awtFormat) { + case BufferedImage.TYPE_3BYTE_BGR: + glFormat = GL2.GL_BGR; + glType = GL2.GL_UNSIGNED_BYTE; + readBackBytes = ByteBuffer.allocate(readBackWidthInPixels * readBackHeightInPixels * 3); + break; + + case BufferedImage.TYPE_INT_RGB: + case BufferedImage.TYPE_INT_ARGB: + glFormat = GL2.GL_BGRA; + glType = (hardwareAccelerationDisabled + ? offscreenContext.getOffscreenContextPixelDataType() + : hwGLFormat); + readBackInts = IntBuffer.allocate(readBackWidthInPixels * readBackHeightInPixels); + break; + + default: + // FIXME: Support more off-screen image types (current + // offscreen context implementations don't use others, and + // some of the OpenGL formats aren't supported in the 1.1 + // headers, which we're currently using) + throw new GLException("Unsupported offscreen image type " + awtFormat); + } + } + } + + if (offscreenImage != null) { + GL2 gl = getGL().getGL2(); + // Save current modes + gl.glGetIntegerv(GL2.GL_PACK_SWAP_BYTES, swapbytes, 0); + gl.glGetIntegerv(GL2.GL_PACK_ROW_LENGTH, rowlength, 0); + gl.glGetIntegerv(GL2.GL_PACK_SKIP_ROWS, skiprows, 0); + gl.glGetIntegerv(GL2.GL_PACK_SKIP_PIXELS, skippixels, 0); + gl.glGetIntegerv(GL2.GL_PACK_ALIGNMENT, alignment, 0); + + gl.glPixelStorei(GL2.GL_PACK_SWAP_BYTES, GL2.GL_FALSE); + gl.glPixelStorei(GL2.GL_PACK_ROW_LENGTH, readBackWidthInPixels); + gl.glPixelStorei(GL2.GL_PACK_SKIP_ROWS, 0); + gl.glPixelStorei(GL2.GL_PACK_SKIP_PIXELS, 0); + gl.glPixelStorei(GL2.GL_PACK_ALIGNMENT, 1); + + // Actually read the pixels. + gl.glReadBuffer(GL2.GL_FRONT); + if (readBackBytes != null) { + gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, glFormat, glType, readBackBytes); + } else if (readBackInts != null) { + gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, glFormat, glType, readBackInts); + } + + // Restore saved modes. + gl.glPixelStorei(GL2.GL_PACK_SWAP_BYTES, swapbytes[0]); + gl.glPixelStorei(GL2.GL_PACK_ROW_LENGTH, rowlength[0]); + gl.glPixelStorei(GL2.GL_PACK_SKIP_ROWS, skiprows[0]); + gl.glPixelStorei(GL2.GL_PACK_SKIP_PIXELS, skippixels[0]); + gl.glPixelStorei(GL2.GL_PACK_ALIGNMENT, alignment[0]); + + if (readBackBytes != null || readBackInts != null) { + // Copy temporary data into raster of BufferedImage for faster + // blitting Note that we could avoid this copy in the cases + // where !offscreenContext.offscreenImageNeedsVerticalFlip(), + // but that's the software rendering path which is very slow + // anyway + Object src = null; + Object dest = null; + int srcIncr = 0; + int destIncr = 0; + + if (readBackBytes != null) { + src = readBackBytes.array(); + dest = ((DataBufferByte) offscreenImage.getRaster().getDataBuffer()).getData(); + srcIncr = readBackWidthInPixels * 3; + destIncr = offscreenImage.getWidth() * 3; + } else { + src = readBackInts.array(); + dest = ((DataBufferInt) offscreenImage.getRaster().getDataBuffer()).getData(); + srcIncr = readBackWidthInPixels; + destIncr = offscreenImage.getWidth(); + } + + if (!hardwareAccelerationDisabled || + offscreenContext.offscreenImageNeedsVerticalFlip()) { + int srcPos = 0; + int destPos = (offscreenImage.getHeight() - 1) * destIncr; + for (; destPos >= 0; srcPos += srcIncr, destPos -= destIncr) { + System.arraycopy(src, srcPos, dest, destPos, destIncr); + } + } else { + int srcPos = 0; + int destEnd = destIncr * offscreenImage.getHeight(); + for (int destPos = 0; destPos < destEnd; srcPos += srcIncr, destPos += destIncr) { + System.arraycopy(src, srcPos, dest, destPos, destIncr); + } + } + + // Note: image will be drawn back in paintComponent() for + // correctness on all platforms + } + } + } else { + // Cause OpenGL pipeline to flush its results because + // otherwise it's possible we will buffer up multiple frames' + // rendering results, resulting in apparent mouse lag + GL2 gl = getGL().getGL2(); + gl.glFinish(); + + postGL(g); + } + } + + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + // This is handled above and dispatched directly to the appropriate context + } + + public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) { + } + } + + class InitAction implements Runnable { + public void run() { + updater.init(GL2JPanel.this); + } + } + private InitAction initAction = new InitAction(); + + class DisplayAction implements Runnable { + public void run() { + updater.display(GL2JPanel.this); + } + } + private DisplayAction displayAction = new DisplayAction(); + + // This one is used exclusively in the non-hardware-accelerated case + class SwapBuffersAction implements Runnable { + public void run() { + offscreenDrawable.swapBuffers(); + } + } + private SwapBuffersAction swapBuffersAction = new SwapBuffersAction(); + + class PaintImmediatelyAction implements Runnable { + public void run() { + paintImmediately(0, 0, getWidth(), getHeight()); + } + } + private PaintImmediatelyAction paintImmediatelyAction = new PaintImmediatelyAction(); + + private int getNextPowerOf2(int number) { + // Workaround for problems where 0 width or height are transiently + // seen during layout + if (number == 0) { + return 2; + } + + if (((number-1) & number) == 0) { + //ex: 8 -> 0b1000; 8-1=7 -> 0b0111; 0b1000&0b0111 == 0 + return number; + } + int power = 0; + while (number > 0) { + number = number>>1; + power++; + } + return (1< An Animator can be attached to one or more {@link + GLAutoDrawable}s to drive their display() methods in a loop.

+ +

The Animator class creates a background thread in which the + calls to display() are performed. After each drawable + has been redrawn, a brief pause is performed to avoid swamping the + CPU, unless {@link #setRunAsFastAsPossible} has been called.

+*/ + +public class Animator { + private volatile ArrayList/**/ 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/**/ 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 remaining elements (as defined by + limit() - position()) 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 remaining elements (as defined by + limit() - position()) 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 remaining elements (as defined by + limit() - position()) 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 remaining elements (as defined by + limit() - position()) 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 remaining elements (as defined by + limit() - position()) 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 remaining elements (as defined by + limit() - position()) 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 remaining elements (as defined by + limit() - position()) 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 remaining elements (as defined by + limit() - position()) 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 remaining elements (as defined by + limit() - position()) 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 remaining elements (as defined by + limit() - position()) 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 remaining elements (as defined by + limit() - position()) 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 remaining elements (as defined by + limit() - position()) 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 remaining elements (as defined by + limit() - position()) 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 remaining elements (as defined by + limit() - position()) 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 FPSAnimator(null, fps). */ + 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 FPSAnimator(null, fps, + scheduleAtFixedRate). */ + 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 + FPSAnimator(null, fps, false). */ + 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.

+ + 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.

+ * + * 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. System.exit() from the application rather than + * rely on the shutdown hook functionality due to inevitable race + * conditions and unspecified behavior during JVM teardown.

+ * + * 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.

+ * + * @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()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 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.*; + +/**

An Animator can be attached to one or more {@link + GLAutoDrawable}s to drive their display() methods in a loop.

+ +

The Animator class creates a background thread in which the + calls to display() are performed. After each drawable + has been redrawn, a brief pause is performed to avoid swamping the + CPU, unless {@link #setRunAsFastAsPossible} has been called.

+*/ + +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.

+ * + * 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").

+ * + * 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 JOGL + * release builds and gluegen-rt-[version]-webstart.zip from the + * GlueGen + * runtime release builds). 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.

+ * + * 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 JOAL + * release builds.

+ * + * Sample applet code: + *

+ * <applet code="com.sun.opengl.util.JOGLAppletLauncher"
+ *      width=600
+ *      height=400
+ *      codebase="/lib"
+ *      archive="jogl.jar,gluegen-rt.jar,your_applet.jar">
+ *   <param name="subapplet.classname" VALUE="untrusted.JOGLApplet">
+ *   <param name="subapplet.displayname" VALUE="My JOGL Applet">
+ *   <param name="progressbar" value="true">
+ *   <param name="cache_archive" VALUE="jogl.jar,gluegen-rt.jar,your_applet.jar">
+ *   <param name="cache_archive_ex" VALUE="jogl.jar;preload,gluegen-rt.jar;preload,your_applet.jar;preload">
+ * </applet>
+ * 
+ *

+ * + * 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.

+ * + * The behavior of the noddraw-related dialog box can be changed via + * two applet parameters. The jogl.silent.noddraw.check + * parameter, if set to "true", 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 jogl.disable.noddraw.check + * applet parameter to "true".

+ * + * 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.

+ * + * 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.

+ * + * 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 + * this + * thread on the javagaming.org forums and + * this + * thread on the Mozilla bug reporting database.

+ * + * @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/**/ jreVersions = new HashSet/**/(); + for (Iterator/**/ 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/**/ 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); + } + } +} + -- cgit v1.2.3