/** * Copyright (c) 2003-2009, Xith3D Project Group all rights reserved. * * Portions based on the Java3D interface, Copyright by Sun Microsystems. * Many thanks to the developers of Java3D and Sun Microsystems for their * innovation and design. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions 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 the 'Xith3D Project Group' nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A * RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE */ package org.xith3d.render.jsr231; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Frame; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Insets; import java.awt.Point; import java.awt.Window; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.IOException; import java.net.URL; import java.util.List; import javax.imageio.ImageIO; import javax.media.opengl.GL; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLCapabilitiesChooser; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLJPanel; import javax.swing.JFrame; import org.jagatoo.input.InputSystem; import org.jagatoo.input.impl.awt.AWTCursorConverter; import org.jagatoo.input.impl.mixed.AWTJInputInputDeviceFactory; import org.jagatoo.input.render.Cursor; import org.jagatoo.logging.ProfileTimer; import org.xith3d.picking.PickRequest; import org.xith3d.render.RenderPass; import org.xith3d.render.config.DisplayMode; import org.xith3d.render.config.FSAA; import org.xith3d.render.config.OpenGLLayer; import org.xith3d.render.config.DisplayMode.FullscreenMode; import org.xith3d.scenegraph.View; import org.xith3d.scenegraph._SG_PrivilegedAccess; import org.xith3d.utility.logging.X3DLog; import com.sun.opengl.util.Gamma; import java.awt.Toolkit; import javax.media.opengl.DebugGL; import javax.media.opengl.TraceGL; /** * The CanvasPeer implementation for the official Java OpenGL Bindings (JOGL) * and Swing. Its essentially CanvasPeerAWTImpl except it creates a GLJPanel * rather than a GLCanvas. * * @author David Yazel [jogl] * @author Marvin Froehlich (aka Qudus) * @author Andrew Hanson [replaced GLCanvas with GLJPanel] */ public class CanvasPeerImplSwing extends CanvasPeerImplBase implements GLEventListener { private static final int RENDER_MODE_CLEAR = 1; private static final int RENDER_MODE_DESTROY = 2; private GLCapabilities gc; private GLCapabilitiesChooser gcc; private GLJPanel glJPanel; private GL gl; private GL plainGL; private View view; private List<RenderPass> renderPasses; private boolean layeredMode; private long frameId; private long nanoTime; private long nanoStep; private PickRequest pickRequest = null; private Object pickResult = null; private int renderMode = 0; private Window window; private boolean closeRequested = false; private boolean isRendering = false; private AWTJInputInputDeviceFactory inputDeviceFactory = null; public AWTJInputInputDeviceFactory getInputDeviceFactory(InputSystem inputSystem) { if (inputDeviceFactory == null) { inputDeviceFactory = new AWTJInputInputDeviceFactory(this, inputSystem.getEventQueue()); } return (inputDeviceFactory); } public final GLJPanel getDrawable() { return (glJPanel); } public void finish() { glJPanel.display(); } public void refreshCursor(org.jagatoo.input.devices.Mouse mouse) { if (getCursor() == null) { glJPanel.setCursor(AWTCursorConverter.HIDDEN_CURSOR); } else if (getCursor() == Cursor.DEFAULT_CURSOR) { glJPanel.setCursor(java.awt.Cursor.getDefaultCursor()); } else// if ( getCursor() != null ) { AWTCursorConverter.convertCursor(getCursor()); glJPanel.setCursor((java.awt.Cursor) getCursor().getCursorObject()); } } public final boolean receivesInputEvents() { return (glJPanel.hasFocus()); } public CanvasPeerImplSwing(Object owner, DisplayMode displayMode, FullscreenMode fullscreen, boolean vsync, FSAA fsaa, int depthBufferSize) { super(displayMode, fullscreen, vsync, fsaa, depthBufferSize); if (owner == null) { String message = "The " + this.getClass().getSimpleName() + " must be used with an owner (integrated into an AWT/Swing environment)."; X3DLog.error(message); throw new Error(message); } try { System.setProperty("org.xith3d.render.jsr231.displayGLInfos", String.valueOf(false)); } catch (SecurityException ignore) { // Ignore a SecurityException for Applet deployment } this.gc = new GLCapabilities(); gc.setStencilBits(8); //gc.setAlphaBits( 8 ); gc.setDepthBits(depthBufferSize); if (fsaa != FSAA.OFF) { gc.setSampleBuffers(true); } gc.setNumSamples(fsaa.getIntValue()); this.gcc = new OldStyleGLCapabilitiesChooser(); this.glJPanel = new GLJPanel(gc, gcc, null); //glJPanel.setBounds( 0, 0, getDisplayMode().getWidth(), getDisplayMode().getHeight() ); glJPanel.setBounds(0, 0, displayMode.getWidth(), displayMode.getHeight()); setVSyncEnabled(vsync); glJPanel.addGLEventListener(this); glJPanel.setFocusable(true); //this.gl = glJPanel.getGL(); clear(); // boomschakalacka: // FIXME: this code is unreachable as Exception is thrown in line 156 if owner is null! if (owner == null) { JFrame frame = new JFrame("Xith3D (JOGL)"); frame.setLayout(null); if (fullscreen.isFullscreen()) { frame.setBackground(Color.BLACK); frame.getContentPane().setBackground(Color.BLACK); } frame.setUndecorated(fullscreen != FullscreenMode.WINDOWED); frame.setSize(displayMode.getWidth(), displayMode.getHeight()); setWindow(frame); frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { closeRequested = true; } }); final boolean exclusive = (fullscreen.isFullscreen() && (displayMode.getNativeMode() != null)); frame.getContentPane().add(glJPanel); frame.setVisible(true); if (!exclusive) { Thread.yield(); final Dimension frameSize; if (!fullscreen.isFullscreen()) { Insets insets = frame.getInsets(); //glJPanel.setLocation( insets.left, insets.top ); frameSize = new Dimension(displayMode.getWidth() + insets.left + insets.right, displayMode.getHeight() + insets.top + insets.bottom); frame.setSize(frameSize); } else { frameSize = new Dimension(displayMode.getWidth(), displayMode.getHeight()); } Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); Point upperLeft = new Point((screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2); frame.setLocation(upperLeft); //frame.setResizable( false/* !fullscreen */); } else { final java.awt.DisplayMode awtMode = (java.awt.DisplayMode) displayMode.getNativeMode(); GraphicsDevice graphDev = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); graphDev.setFullScreenWindow(frame); graphDev.setDisplayMode(awtMode); } } else { glJPanel.setBounds(0, 0, displayMode.getWidth(), displayMode.getHeight()); ((Container) owner).add(glJPanel); } Thread.yield(); // boomschakalacka: // FIXME: this code is unreachable as Exception is thrown in line 156 if owner is null! if (owner == null) { /* java.awt.Point loc = glCanvas.getLocation(); glCanvas.setLocation( loc.x + 1, loc.y + 1 ); */ ((Frame) window).setResizable(false); java.awt.Dimension size = window.getSize(); window.setSize(size.width - 2, size.height - 2); } } protected void updateTraceDebug() { if (!(gl instanceof DebugGL || gl instanceof TraceGL)) { plainGL = gl; } final boolean needsDebugGL = getRenderPeer().getRenderOptions().areGLErrorChecksEnabled(); final boolean needsTraceGL = getRenderPeer().getRenderOptions().isGLTracingEnabled(); if ((needsTraceGL && needsDebugGL) && !(gl instanceof TraceGL)) { X3DLog.debug("OpenGL error check and command tracing are enabled"); gl = new TraceGL(new DebugGL(plainGL), System.err); } else if (needsTraceGL && !(gl instanceof TraceGL)) { X3DLog.debug("OpenGL command tracing is enabled"); gl = new TraceGL(plainGL, System.err); } else if (needsDebugGL && !(gl instanceof DebugGL)) { X3DLog.debug("OpenGL error check is enabled"); gl = new DebugGL(plainGL); } else { gl = plainGL; } glJPanel.setGL(gl); } /** * {@inheritDoc} */ public final void init(GLAutoDrawable drawable) { this.gl = glJPanel.getGL(); updateTraceDebug(); /* * The GLJPanel recreates the whole GL-context when it is reinitialized. * This can happen at anytime, for example during a hide/show/resize * on their container. We need to destroy the GL-names to make them be * recreated on the next render. */ destroyGLNames(false); super.init(); } /** * {@inheritDoc} */ @Override public OpenGLLayer getType() { return (OpenGLLayer.JOGL_SWING); } /** * {@inheritDoc} */ @Override public void setVSyncEnabled(boolean vsync) { super.setVSyncEnabled(vsync); setSwapInterval(vsync ? 1 : 0); } @Override public final GL getGL() { return (gl); } protected final void setWindow(Window w) { this.window = w; } /** * {@inheritDoc} */ @Override public final Window getWindow() { return (window); } /** * {@inheritDoc} */ @Override public final Component getComponent() { return (glJPanel); } /** * {@inheritDoc} */ @Override public void setIcon(URL iconResource) throws IOException { assert (window instanceof Frame) : "Window isn't a Frame"; ((Frame) window).setIconImage(ImageIO.read(iconResource)); } /** * {@inheritDoc} */ @Override public final void setTitle(String title) { if (window != null) { assert window instanceof Frame : "Window isn't a Frame"; ((Frame) window).setTitle(title); } else { // Do nothing... } } /** * {@inheritDoc} */ @Override public final String getTitle() { if (window == null) { return (""); } assert window instanceof Frame : "Window isn't a Frame"; return (((Frame) window).getTitle()); } /** * {@inheritDoc} */ @Override public final boolean setLocation(int x, int y) { if ((window.getLocation().x == x) && (window.getLocation().y == y)) { return (false); } window.setLocation(x, y); return (true); } /** * {@inheritDoc} */ @Override public final int getLeft() { return (glJPanel.getLocationOnScreen().x); } /** * {@inheritDoc} */ @Override public final int getTop() { return (glJPanel.getLocationOnScreen().y); } /** * {@inheritDoc} */ @Override public final boolean setSize(int width, int height) { if ((glJPanel.getSize().width != width) || (glJPanel.getSize().height != height)) { glJPanel.setSize(width, height); if (getWindow() != null) { Insets insets = getWindow().getInsets(); glJPanel.setLocation(insets.left, insets.top); Dimension frameSize = new Dimension(width + insets.left + insets.right, height + insets.top + insets.bottom); getWindow().setSize(frameSize); } setDisplayMode(new DisplayMode(getType(), null, width, height, getBPP(), getFrequency())); return (true); } return (false); } /** * {@inheritDoc} */ @Override public final int getWidth() { return (glJPanel.getWidth()); } /** * {@inheritDoc} */ @Override public final int getHeight() { return (glJPanel.getHeight()); } /** * {@inheritDoc} */ @Override protected boolean setDisplayModeImpl(DisplayMode displayMode) { //final boolean result = !displayMode.equals( getDisplayMode() ); final boolean result = true; /* if ( result ) displayModeChanged = true; */ setSize(displayMode.getWidth(), displayMode.getHeight()); return (result); } /** * {@inheritDoc} */ public final void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { _SG_PrivilegedAccess.incGlobalOptionsChangeId(); this.gl = drawable.getGL(); /* ((GLCanvas)drawable).repaint(); gl.glViewport( 0, 0, width, height ); // Reset The Current Viewport gl.glMatrixMode( GL.GL_PROJECTION ); // Select The Projection Matrix gl.glLoadIdentity(); // Reset The Projection Matrix // Calculate The Aspect Ratio Of The Window glu.gluPerspective( 54.0f, width / height, 0.001f, 100.0f ); gl.glMatrixMode( GL.GL_MODELVIEW ); // Select The Modelview Matrix gl.glLoadIdentity(); */ } /** * {@inheritDoc} */ @Override public void setGamma(float gamma, float brightness, float contrast) { super.setGamma(gamma, brightness, contrast); try { Gamma.setDisplayGamma(gamma, brightness, contrast); } catch (Throwable t) { t.printStackTrace(); } } /** * {@inheritDoc} */ public final void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) { } @Override protected void setAutoSwapBufferMode(boolean mode) { glJPanel.setAutoSwapBufferMode(mode); } /** * {@inheritDoc} */ @Override public final boolean isRendering() { return (isRendering); } /** * {@inheritDoc} */ @Override public void beforeThreadChanged() { } /** * {@inheritDoc} */ public final void display(GLAutoDrawable drawable) { { synchronized (getRenderLock()) { if (!isInitialized() || isRendering) { return; } isRendering = true; /* * Reset the ShapeAtomPeer's TransformGroup-id indicator to setup the * modelview matrix at least once per frame. */ ShapeAtomPeer.reset(); if (closeRequested) { closeRequested = false; fireClosingEvent(); } this.gl = drawable.getGL(); if (swapIntervalChanged) { swapIntervalChanged = false; gl.setSwapInterval(getSwapInterval()); } if (renderMode != 0) { boolean proceed = true; if ((renderMode & RENDER_MODE_CLEAR) != 0) { super.clear(); isRendering = false; proceed = false; } if ((renderMode & RENDER_MODE_DESTROY) != 0) { destroy(); isRendering = false; proceed = false; } if (!proceed) { return; } } pickResult = doRender(view, renderPasses, layeredMode, frameId, nanoTime, nanoStep, pickRequest); isRendering = false; } } } /** * {@inheritDoc} */ @Override protected Object initRenderingImpl(View view, List<RenderPass> renderPasses, boolean layeredMode, long frameId, long nanoTime, long nanoStep, PickRequest pickRequest) { ProfileTimer.startProfile(X3DLog.LOG_CHANNEL, "CanvasPeerImpl::initRendering"); this.renderMode = 0; this.view = view; this.renderPasses = renderPasses; this.layeredMode = layeredMode; this.frameId = frameId; this.nanoTime = nanoTime; this.nanoStep = nanoStep; this.pickRequest = pickRequest; this.pickResult = null; Object result = this.pickResult; this.pickResult = null; ProfileTimer.endProfile(); return (result); } /** * {@inheritDoc} */ @Override public void clear() { renderMode |= RENDER_MODE_CLEAR; glJPanel.display(); } /** * {@inheritDoc} */ @Override public void destroy() { if ((renderMode & RENDER_MODE_DESTROY) == 0) { renderMode |= RENDER_MODE_CLEAR | RENDER_MODE_DESTROY; if (isGammaChanged) { try { Gamma.resetDisplayGamma(); } catch (IllegalArgumentException e) { // if gamma has not been corrected, this method throws an IllegalArgumentException! //e.printStackTrace(); } } // FIXME: NVIDIA drivers don't seem to like this //glCanvas.getContext().destroy(); glJPanel.display(); } else { super.destroy(); if (getWindow() != null) { getWindow().dispose(); } } } }