1/* 2 * Copyright (c) 2009-2010 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33package com.jme3.system.lwjgl; 34 35import com.jme3.system.AppSettings; 36import com.jme3.system.JmeContext.Type; 37import java.awt.Graphics2D; 38import java.awt.image.BufferedImage; 39import java.nio.ByteBuffer; 40import java.util.concurrent.atomic.AtomicBoolean; 41import java.util.logging.Level; 42import java.util.logging.Logger; 43import org.lwjgl.LWJGLException; 44import org.lwjgl.opengl.*; 45 46public class LwjglDisplay extends LwjglAbstractDisplay { 47 48 private static final Logger logger = Logger.getLogger(LwjglDisplay.class.getName()); 49 50 private final AtomicBoolean needRestart = new AtomicBoolean(false); 51 private PixelFormat pixelFormat; 52 53 protected DisplayMode getFullscreenDisplayMode(int width, int height, int bpp, int freq){ 54 try { 55 DisplayMode[] modes = Display.getAvailableDisplayModes(); 56 for (DisplayMode mode : modes){ 57 if (mode.getWidth() == width 58 && mode.getHeight() == height 59 && (mode.getBitsPerPixel() == bpp || (bpp==24&&mode.getBitsPerPixel()==32)) 60 && mode.getFrequency() == freq){ 61 return mode; 62 } 63 } 64 } catch (LWJGLException ex) { 65 listener.handleError("Failed to acquire fullscreen display mode!", ex); 66 } 67 return null; 68 } 69 70 protected void createContext(AppSettings settings) throws LWJGLException{ 71 DisplayMode displayMode = null; 72 if (settings.getWidth() <= 0 || settings.getHeight() <= 0){ 73 displayMode = Display.getDesktopDisplayMode(); 74 settings.setResolution(displayMode.getWidth(), displayMode.getHeight()); 75 }else if (settings.isFullscreen()){ 76 displayMode = getFullscreenDisplayMode(settings.getWidth(), settings.getHeight(), 77 settings.getBitsPerPixel(), settings.getFrequency()); 78 if (displayMode == null) 79 throw new RuntimeException("Unable to find fullscreen display mode matching settings"); 80 }else{ 81 displayMode = new DisplayMode(settings.getWidth(), settings.getHeight()); 82 } 83 84 int samples = 0; 85 if (settings.getSamples() > 1){ 86 samples = settings.getSamples(); 87 } 88 PixelFormat pf = new PixelFormat(settings.getBitsPerPixel(), 89 0, 90 settings.getDepthBits(), 91 settings.getStencilBits(), 92 samples); 93 94 frameRate = settings.getFrameRate(); 95 logger.log(Level.INFO, "Selected display mode: {0}", displayMode); 96 97 boolean pixelFormatChanged = false; 98 if (created.get() && (pixelFormat.getBitsPerPixel() != pf.getBitsPerPixel() 99 ||pixelFormat.getDepthBits() != pf.getDepthBits() 100 ||pixelFormat.getStencilBits() != pf.getStencilBits() 101 ||pixelFormat.getSamples() != pf.getSamples())){ 102 renderer.resetGLObjects(); 103 Display.destroy(); 104 pixelFormatChanged = true; 105 } 106 pixelFormat = pf; 107 108 Display.setTitle(settings.getTitle()); 109 if (displayMode != null){ 110 if (settings.isFullscreen()){ 111 Display.setDisplayModeAndFullscreen(displayMode); 112 }else{ 113 Display.setFullscreen(false); 114 Display.setDisplayMode(displayMode); 115 } 116 }else{ 117 Display.setFullscreen(settings.isFullscreen()); 118 } 119 120 if (settings.getIcons() != null) { 121 Display.setIcon(imagesToByteBuffers(settings.getIcons())); 122 } 123 124 Display.setVSyncEnabled(settings.isVSync()); 125 126 if (created.get() && !pixelFormatChanged){ 127 Display.releaseContext(); 128 Display.makeCurrent(); 129 Display.update(); 130 } 131 132 if (!created.get() || pixelFormatChanged){ 133 ContextAttribs attr = createContextAttribs(); 134 if (attr != null){ 135 Display.create(pixelFormat, attr); 136 }else{ 137 Display.create(pixelFormat); 138 } 139 renderable.set(true); 140 141 if (pixelFormatChanged && pixelFormat.getSamples() > 1 142 && GLContext.getCapabilities().GL_ARB_multisample){ 143 GL11.glEnable(ARBMultisample.GL_MULTISAMPLE_ARB); 144 } 145 } 146 } 147 148 protected void destroyContext(){ 149 try { 150 renderer.cleanup(); 151 Display.releaseContext(); 152 Display.destroy(); 153 } catch (LWJGLException ex) { 154 listener.handleError("Failed to destroy context", ex); 155 } 156 } 157 158 public void create(boolean waitFor){ 159 if (created.get()){ 160 logger.warning("create() called when display is already created!"); 161 return; 162 } 163 164 new Thread(this, "LWJGL Renderer Thread").start(); 165 if (waitFor) 166 waitFor(true); 167 } 168 169 @Override 170 public void runLoop(){ 171 // This method is overriden to do restart 172 if (needRestart.getAndSet(false)){ 173 try{ 174 createContext(settings); 175 }catch (LWJGLException ex){ 176 logger.log(Level.SEVERE, "Failed to set display settings!", ex); 177 } 178 listener.reshape(settings.getWidth(), settings.getHeight()); 179 logger.info("Display restarted."); 180 } 181 182 super.runLoop(); 183 } 184 185 @Override 186 public void restart() { 187 if (created.get()){ 188 needRestart.set(true); 189 }else{ 190 logger.warning("Display is not created, cannot restart window."); 191 } 192 } 193 194 public Type getType() { 195 return Type.Display; 196 } 197 198 public void setTitle(String title){ 199 if (created.get()) 200 Display.setTitle(title); 201 } 202 203 private ByteBuffer[] imagesToByteBuffers(Object[] images) { 204 ByteBuffer[] out = new ByteBuffer[images.length]; 205 for (int i = 0; i < images.length; i++) { 206 BufferedImage image = (BufferedImage) images[i]; 207 out[i] = imageToByteBuffer(image); 208 } 209 return out; 210 } 211 212 private ByteBuffer imageToByteBuffer(BufferedImage image) { 213 if (image.getType() != BufferedImage.TYPE_INT_ARGB_PRE) { 214 BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB_PRE); 215 Graphics2D g = convertedImage.createGraphics(); 216 double width = image.getWidth() * (double) 1; 217 double height = image.getHeight() * (double) 1; 218 g.drawImage(image, (int) ((convertedImage.getWidth() - width) / 2), 219 (int) ((convertedImage.getHeight() - height) / 2), 220 (int) (width), (int) (height), null); 221 g.dispose(); 222 image = convertedImage; 223 } 224 225 byte[] imageBuffer = new byte[image.getWidth() * image.getHeight() * 4]; 226 int counter = 0; 227 for (int i = 0; i < image.getHeight(); i++) { 228 for (int j = 0; j < image.getWidth(); j++) { 229 int colorSpace = image.getRGB(j, i); 230 imageBuffer[counter + 0] = (byte) ((colorSpace << 8) >> 24); 231 imageBuffer[counter + 1] = (byte) ((colorSpace << 16) >> 24); 232 imageBuffer[counter + 2] = (byte) ((colorSpace << 24) >> 24); 233 imageBuffer[counter + 3] = (byte) (colorSpace >> 24); 234 counter += 4; 235 } 236 } 237 return ByteBuffer.wrap(imageBuffer); 238 } 239 240} 241