/** * 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.Toolkit; 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.DebugGL; import javax.media.opengl.GL; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCanvas; import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLCapabilitiesChooser; import javax.media.opengl.GLContext; import javax.media.opengl.GLEventListener; import javax.media.opengl.TraceGL; 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.utility.logging.X3DLog; import com.sun.opengl.util.Gamma; /** * The CanvasPeer implementation for the official Java OpenGL Bindings (JOGL) * and AWT. * * @author David Yazel [jogl] * @author Marvin Froehlich (aka Qudus) */ public class CanvasPeerImplAWT 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 GLCanvas glCanvas; 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 boolean isRendering = false; private Frame window; private boolean closeRequested = false; private AWTJInputInputDeviceFactory inputDeviceFactory = null; public AWTJInputInputDeviceFactory getInputDeviceFactory( InputSystem inputSystem ) { if ( inputDeviceFactory == null ) { inputDeviceFactory = new AWTJInputInputDeviceFactory( this, inputSystem.getEventQueue() ); } return ( inputDeviceFactory ); } public final GLCanvas getDrawable() { return ( glCanvas ); } public void refreshCursor( org.jagatoo.input.devices.Mouse mouse ) { if ( !mouse.isAbsolute() || ( getCursor() == null ) ) { glCanvas.setCursor( AWTCursorConverter.HIDDEN_CURSOR ); } else if ( getCursor() == Cursor.DEFAULT_CURSOR ) { glCanvas.setCursor( java.awt.Cursor.getDefaultCursor() ); } else// if ( getCursor() != null ) { AWTCursorConverter.convertCursor( getCursor() ); glCanvas.setCursor( (java.awt.Cursor)getCursor().getCursorObject() ); } } public final boolean receivesInputEvents() { return ( glCanvas.hasFocus() ); } public CanvasPeerImplAWT( Object owner, DisplayMode displayMode, FullscreenMode fullscreen, boolean vsync, FSAA fsaa, int depthBufferSize ) { super( displayMode, fullscreen, vsync, fsaa, depthBufferSize ); 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.glCanvas = new GLCanvas( gc, gcc, null, null ); //glCanvas.setBounds( 0, 0, getDisplayMode().getWidth(), getDisplayMode().getHeight() ); glCanvas.setBounds( 0, 0, displayMode.getWidth(), displayMode.getHeight() ); setVSyncEnabled( vsync ); //canvas = GLDrawableFactory.getFactory().createGLCanvas( gc, gcc ); //canvas.setNoAutoRedrawMode( true ); glCanvas.addGLEventListener( this ); glCanvas.setFocusable( true ); this.gl = glCanvas.getGL(); updateTraceDebug(); clear(); Point upperLeft = null; if ( owner == null ) { Frame frame = new Frame( "Xith3D (JOGL)" ); frame.setLayout( null ); if ( fullscreen.isFullscreen() ) { frame.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.add( glCanvas ); frame.setVisible( true ); if ( !exclusive ) { Thread.yield(); final Dimension frameSize; if ( !fullscreen.isFullscreen() ) { Insets insets = frame.getInsets(); glCanvas.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(); 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 { glCanvas.setBounds( 0, 0, displayMode.getWidth(), displayMode.getHeight() ); ( (Container)owner ).add( glCanvas ); } Thread.yield(); 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 ); Thread.yield(); if ( ( window.getLocation().x == 0 ) && ( window.getLocation().y == 0 ) && ( upperLeft != null ) ) window.setLocation( upperLeft ); } } 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; glCanvas.setGL( gl ); } /** * {@inheritDoc} */ public final void init( GLAutoDrawable drawable ) { this.gl = glCanvas.getGL(); updateTraceDebug(); /* * The GLCanvas 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_AWT ); } /** * {@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( Frame w ) { this.window = w; } /** * {@inheritDoc} */ @Override public final Frame getWindow() { return ( window ); } /** * {@inheritDoc} */ @Override public final Component getComponent() { return ( glCanvas ); } /** * {@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 ( glCanvas.getLocationOnScreen().x ); } /** * {@inheritDoc} */ @Override public final int getTop() { return ( glCanvas.getLocationOnScreen().y ); } /** * {@inheritDoc} */ @Override public final boolean setSize( int width, int height ) { if ( ( glCanvas.getSize().width != width ) || ( glCanvas.getSize().height != height ) ) { glCanvas.setSize( width, height ); if ( getWindow() != null ) { Insets insets = getWindow().getInsets(); glCanvas.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 ( glCanvas.getWidth() ); } /** * {@inheritDoc} */ @Override public final int getHeight() { return ( glCanvas.getHeight() ); } /** * {@inheritDoc} */ @Override public final boolean setFullscreen( boolean fullscreen ) { final boolean oldState = super.setFullscreen( fullscreen ); if ( getFullscreenSwitchRequest() != null ) { final boolean newFSMode = getFullscreenSwitchRequest().booleanValue(); final boolean exclusive = ( newFSMode && ( getDisplayMode().getNativeMode() != null ) ); if ( !exclusive ) { GraphicsDevice graphDev = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); graphDev.setFullScreenWindow( null ); Thread.yield(); final Dimension frameSize; if ( !newFSMode ) { Insets insets = window.getInsets(); glCanvas.setLocation( insets.left, insets.top ); frameSize = new Dimension( getWidth() + insets.left + insets.right, getHeight() + insets.top + insets.bottom ); window.setSize( frameSize ); } else { frameSize = new Dimension( getWidth(), getHeight() ); } Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); Point upperLeft = new Point( ( screenSize.width - frameSize.width ) / 2, ( screenSize.height - frameSize.height ) / 2 ); window.setLocation( upperLeft ); //frame.setResizable( false/* !fullscreen */); } else { final java.awt.DisplayMode awtMode = (java.awt.DisplayMode)getDisplayMode().getNativeMode(); GraphicsDevice graphDev = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); graphDev.setFullScreenWindow( window ); graphDev.setDisplayMode( awtMode ); } if ( !newFSMode ) { final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); setLocation( ( ( screenSize.width - getWidth() ) / 2 ), ( ( screenSize.height - getHeight() ) / 2 ) ); } resetFullscreenSwitchRequest(); } return ( oldState ); } /** * {@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 ) { this.gl = drawable.getGL(); updateTraceDebug(); /* ((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 ) { glCanvas.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(); updateTraceDebug(); 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; } } @Override public void finish() { glCanvas.display(); } /** * {@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; // glCanvas.display(); Object result = this.pickResult; this.pickResult = null; ProfileTimer.endProfile(); return ( result ); } /** * {@inheritDoc} */ @Override public void clear() { renderMode |= RENDER_MODE_CLEAR; glCanvas.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(); /* * This may be a possible solution for destroying the glCanvas. */ GLContext context = glCanvas.getContext(); if ( context.makeCurrent() != GLContext.CONTEXT_NOT_CURRENT ) { context.release(); context.destroy(); } glCanvas.display(); } else { super.destroy(); if ( getWindow() != null ) getWindow().dispose(); } } }