/**
* 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.lwjgl;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.List;
import javax.imageio.ImageIO;
import org.jagatoo.image.SharedBufferedImage;
import org.jagatoo.input.InputSystem;
import org.jagatoo.input.impl.lwjgl.LWJGLCursorConverter;
import org.jagatoo.input.impl.lwjgl.LWJGLMessageProcessor;
//import org.jagatoo.input.impl.lwjgl.LWJGLInputDeviceFactory;
import org.jagatoo.input.impl.mixed.LWJGLJInputInputDeviceFactory;
import org.jagatoo.input.render.Cursor;
import org.jagatoo.logging.ProfileTimer;
import org.jagatoo.util.image.ImageUtility;
import org.jagatoo.util.nio.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.PixelFormat;
import org.xith3d.picking.PickRequest;
import org.xith3d.render.RenderPass;
import org.xith3d.render.config.DisplayMode;
import org.xith3d.render.config.DisplayModeSelector;
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;
/**
* The CanvasPeer implementation for the LightWeight Java Game Library (LWJGL)
*
* @author David Yazel
* @author Marvin Froehlich (aka Qudus)
* @author Amos Wenger (aka BlueSky)
*/
public class CanvasPeerImplNative extends CanvasPeerImplBase
{
private static final boolean NON_INTERACTIVE_MODE = false;
private int left = 0;
private int top = 0;
private int width;
private int height;
private boolean displayModeChanged = false;
private boolean isRendering = false;
private static final long PROCESS_MESSAGE_TIME_INTERVAL = 100000000L; // 100 milli-seconds
private long lastIsCloseRequestedQueryTime = 0L;
private LWJGLJInputInputDeviceFactory inputDeviceFactory = null;
public LWJGLJInputInputDeviceFactory getInputDeviceFactory( InputSystem inputSystem )
{
if ( inputDeviceFactory == null )
{
//inputDeviceFactory = new LWJGLInputDeviceFactory( this, inputSystem.getEventQueue() );
inputDeviceFactory = new LWJGLJInputInputDeviceFactory( this, inputSystem.getEventQueue() );
}
return ( inputDeviceFactory );
}
public final Object getDrawable()
{
return ( null );
}
public void refreshCursor( org.jagatoo.input.devices.Mouse mouse )
{
try
{
if ( !org.lwjgl.input.Mouse.isCreated() )
org.lwjgl.input.Mouse.create();
if ( getCursor() == null )
{
if ( mouse.isAbsolute() )
org.lwjgl.input.Mouse.setGrabbed( true );
}
else
{
if ( mouse.isAbsolute() )
org.lwjgl.input.Mouse.setGrabbed( false );
}
if ( getCursor() == Cursor.DEFAULT_CURSOR )
{
org.lwjgl.input.Mouse.setNativeCursor( null );
}
else if ( getCursor() != null )
{
LWJGLCursorConverter.convertCursor( getCursor() );
org.lwjgl.input.Mouse.setNativeCursor( (org.lwjgl.input.Cursor)getCursor().getCursorObject() );
}
}
catch ( org.lwjgl.LWJGLException e )
{
throw new Error( e );
}
}
public final boolean receivesInputEvents()
{
return ( true );
}
/**
*
* @param owner
* @param displayMode
* @param fullscreen
* @param vsync
* @param fsaa
* @param depthBufferSize
*/
public CanvasPeerImplNative( Object owner, DisplayMode displayMode, FullscreenMode fullscreen, boolean vsync, FSAA fsaa, int depthBufferSize )
{
super( displayMode, fullscreen, vsync, fsaa, depthBufferSize );
try
{
if ( this.isUndecorated() )
System.setProperty( "org.lwjgl.opengl.Window.undecorated", String.valueOf( true ) );
else
System.setProperty( "org.lwjgl.opengl.Window.undecorated", String.valueOf( false ) );
}
catch ( SecurityException ignore )
{
// Ignore a SecurityException for Applet deployment
}
try
{
System.setProperty( "org.xith3d.render.lwjgl.displayGLInfos", String.valueOf( false ) );
}
catch ( SecurityException ignore )
{
// Ignore a SecurityException for Applet deployment
}
this.width = getDisplayMode().getWidth();
this.height = getDisplayMode().getHeight();
try
{
Display.setDisplayMode( getNativeDisplayMode() );
Display.setFullscreen( fullscreen.isFullscreen() );
Display.setTitle( "Xith3D (LWJGL)" );
Display.setVSyncEnabled( vsync );
Display.create( new PixelFormat( 0, depthBufferSize, 8, fsaa.getIntValue() ) );
if ( !fullscreen.isFullscreen() )
{
final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setLocation( ( ( screenSize.width - width ) / 2 ), ( ( screenSize.height - height ) / 2 ) );
}
clear();
beforeThreadChanged();
init();
}
catch ( Throwable t )
{
if ( t instanceof Error )
throw (Error)t;
else if ( t instanceof RuntimeException )
throw (RuntimeException)t;
else
throw new Error( t.getMessage(), t );
}
if ( NON_INTERACTIVE_MODE )
{
LWJGLMessageProcessor.allowOneUpdate();
}
}
@Override
protected final void init() throws Throwable
{
Display.makeCurrent();
super.init();
Display.releaseContext();
}
/**
* {@inheritDoc}
*/
@Override
public OpenGLLayer getType()
{
return ( OpenGLLayer.LWJGL );
}
@Override
protected void applyVSync()
{
Display.setVSyncEnabled( isVSyncEnabled() );
}
/**
* {@inheritDoc}
*/
@Override
public void setVSyncEnabled( boolean vsync )
{
super.setVSyncEnabled( vsync );
vsyncSwitched = true;
}
/**
* {@inheritDoc}
*/
@Override
public final Object getWindow()
{
//throw new UnsupportedOperationException( "LWJGL does not create a Window." );
return ( null );
}
/**
* {@inheritDoc}
*/
@Override
public final Object getComponent()
{
//throw new UnsupportedOperationException( "LWJGL does not create a Component." ) );
return ( null );
}
private static ByteBuffer[] createIcon( URL url ) throws IOException
{
BufferedImage srcImg = ImageIO.read( url );
String osName = System.getProperty( "os.name" ).toLowerCase();
ByteBuffer[] bbs;
if ( osName.indexOf( "windows" ) >= 0 )
{
SharedBufferedImage trgImg0 = SharedBufferedImage.create( 16, 16, 4, true, new int[] { 0, 1, 2, 3 }, null );
ImageUtility.scaleImage( srcImg, trgImg0 );
ByteBuffer bb0 = BufferUtils.createByteBuffer( trgImg0.getWidth() * trgImg0.getHeight() * 4 );
bb0.put( trgImg0.getSharedData() );
bb0.flip();
SharedBufferedImage trgImg1 = SharedBufferedImage.create( 32, 32, 4, true, new int[] { 0, 1, 2, 3 }, null );
ImageUtility.scaleImage( srcImg, trgImg1 );
ByteBuffer bb1 = BufferUtils.createByteBuffer( trgImg1.getWidth() * trgImg1.getHeight() * 4 );
bb1.put( trgImg1.getSharedData() );
bb1.flip();
bbs = new ByteBuffer[] { bb0, bb1 };
}
else if ( osName.indexOf( "mac" ) >= 0 )
{
SharedBufferedImage trgImg0 = SharedBufferedImage.create( 128, 128, 4, true, new int[] { 0, 1, 2, 3 }, null );
ImageUtility.scaleImage( srcImg, trgImg0 );
ByteBuffer bb0 = BufferUtils.createByteBuffer( trgImg0.getWidth() * trgImg0.getHeight() * 4 );
bb0.put( trgImg0.getSharedData() );
bb0.flip();
bbs = new ByteBuffer[] { bb0 };
}
else // expect linux or similar platform
{
SharedBufferedImage trgImg0 = SharedBufferedImage.create( 32, 32, 4, true, new int[] { 0, 1, 2, 3 }, null );
ImageUtility.scaleImage( srcImg, trgImg0 );
ByteBuffer bb0 = BufferUtils.createByteBuffer( trgImg0.getWidth() * trgImg0.getHeight() * 4 );
bb0.put( trgImg0.getSharedData() );
bb0.flip();
bbs = new ByteBuffer[] { bb0 };
}
return ( bbs );
}
/**
* {@inheritDoc}
*/
@Override
public void setIcon( URL iconResource ) throws IOException
{
Display.setIcon( createIcon( iconResource ) );
}
/**
* {@inheritDoc}
*/
@Override
public final void setTitle( String title )
{
/*
try
{
Display.releaseContext();
System.err.println( Thread.currentThread() );
Thread.dumpStack();
Display.makeCurrent();
}
catch ( Throwable t )
{
t.printStackTrace();
}
*/
Display.setTitle( title );
/*
try
{
Display.releaseContext();
}
catch ( Throwable t )
{
t.printStackTrace();
}
*/
}
/**
* {@inheritDoc}
*/
@Override
public final String getTitle()
{
return ( Display.getTitle() );
}
/**
* {@inheritDoc}
*/
@Override
public final boolean setLocation( int x, int y )
{
if ( ( this.left == x ) && ( this.top == y ) )
{
return ( false );
}
Display.setLocation( x, y );
this.left = x;
this.top = y;
return ( true );
}
/**
* {@inheritDoc}
*/
@Override
public final int getLeft()
{
return ( left );
}
/**
* {@inheritDoc}
*/
@Override
public final int getTop()
{
return ( top );
}
/**
* {@inheritDoc}
*/
@Override
public final boolean setSize( int width, int height )
{
if ( ( width == this.width ) && ( height == this.height ) )
return ( false );
DisplayMode displayMode = DisplayModeSelector.getImplementation( OpenGLLayer.LWJGL ).getBestMode( width, height, getBPP(), getFrequency() );
if ( displayMode == null )
return ( false );
setDisplayModeRef( displayMode );
displayModeChanged = true;
this.width = getDisplayMode().getWidth();
this.height = getDisplayMode().getHeight();
return ( true );
}
/**
* {@inheritDoc}
*/
@Override
public final int getWidth()
{
return ( width );
}
/**
* {@inheritDoc}
*/
@Override
public final int getHeight()
{
return ( height );
}
/**
* {@inheritDoc}
*/
@Override
protected boolean setDisplayModeImpl( DisplayMode displayMode )
{
//final boolean result = !displayMode.equals( getDisplayMode() );
final boolean result = true;
if ( result )
{
displayModeChanged = true;
this.width = displayMode.getWidth();
this.height = displayMode.getHeight();
}
return ( result );
}
/**
* {@inheritDoc}
*/
@Override
public void setGamma( float gamma, float brightness, float contrast )
{
super.setGamma( gamma, brightness, contrast );
try
{
Display.setDisplayConfiguration( gamma, brightness, contrast );
}
catch ( LWJGLException e )
{
e.printStackTrace();
}
}
/**
* {@inheritDoc}
*/
@Override
public final boolean isRendering()
{
return ( isRendering );
}
/**
* {@inheritDoc}
*/
@Override
protected Thread makeCurrent()
{
try
{
Display.makeCurrent();
}
catch ( Throwable e )
{
e.printStackTrace();
}
return ( Thread.currentThread() );
}
/**
* {@inheritDoc}
*/
@Override
public void beforeThreadChanged()
{
if ( ( renderingThread != null ) || ( getRenderedFrames() == 0L ) )
{
try
{
Display.releaseContext();
}
catch ( Throwable e )
{
e.printStackTrace();
}
renderingThread = null;
}
}
/**
* {@inheritDoc}
*/
@Override
protected Object initRenderingImpl( View view, List< RenderPass > renderPasses, boolean layeredMode, long frameId, long nanoTime, long nanoStep, PickRequest pickRequest )
{
if ( !isInitialized() )
return ( null );
if ( getFullscreenSwitchRequest() != null )
{
try
{
final boolean newFSMode = getFullscreenSwitchRequest().booleanValue();
//destroyGLNames( false );
//Display.destroy();
Display.setFullscreen( newFSMode );
//Display.create( new PixelFormat( 0, getDepthBufferSize(), 8, getFSAA().getIntValue() ) );
if ( newFSMode )
{
this.left = 0;
this.top = 0;
}
else
{
this.left = Display.getDisplayMode().getWidth() / 2;
this.top = 0;
final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Display.setLocation( ( ( screenSize.width - width ) / 2 ), ( ( screenSize.height - height ) / 2 ) );
}
}
catch ( LWJGLException e )
{
e.printStackTrace();
}
resetFullscreenSwitchRequest();
}
if ( displayModeChanged )
{
try
{
final int currentCenterX = left + ( Display.getDisplayMode().getWidth() / 2 );
final int currentCenterY = top + ( Display.getDisplayMode().getHeight() / 2 );
Display.setDisplayMode( getNativeDisplayMode() );
if ( !isFullscreen() )
{
this.left = currentCenterX - ( getNativeDisplayMode().getWidth() / 2 );
this.top = currentCenterY - ( getNativeDisplayMode().getHeight() / 2 );
Display.setLocation( this.left, this.top );
}
}
catch ( LWJGLException e )
{
e.printStackTrace();
}
displayModeChanged = false;
}
ProfileTimer.startProfile( X3DLog.LOG_CHANNEL, "CanvasPeerImpl::render" );
boolean pmTimeHit = ( nanoTime - lastIsCloseRequestedQueryTime ) >= PROCESS_MESSAGE_TIME_INTERVAL;
if ( isClosingListenerRegistered() && pmTimeHit )
{
if ( Display.isCloseRequested() )
{
fireClosingEvent();
}
}
isRendering = true;
Object result = doRender( view, renderPasses, layeredMode, frameId, nanoTime, nanoStep, pickRequest );
isRendering = false;
if ( ( ( frameId % 100 ) == 0 ) || pmTimeHit )
{
Display.processMessages();
if ( NON_INTERACTIVE_MODE )
{
LWJGLMessageProcessor.allowOneUpdate();
}
}
if ( pmTimeHit )
{
lastIsCloseRequestedQueryTime = nanoTime;
}
// updates LWJGL display
//Display.update();
//if ( Display.isVisible() || Display.isDirty() )
//if ( Display.isVisible() )
{
try
{
Display.swapBuffers();
}
catch ( LWJGLException e )
{
X3DLog.print( e );
e.printStackTrace();
}
}
ProfileTimer.endProfile();
return ( result );
}
/**
* {@inheritDoc}
*/
@Override
public void destroy()
{
super.destroy();
try
{
Display.releaseContext();
Display.makeCurrent();
if ( Display.isCreated() && Display.isActive() )
Display.destroy();
}
catch ( Throwable t )
{
//t.printStackTrace();
}
}
}