diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java index 8fdde51af..96895b255 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java @@ -31,6 +31,7 @@ */ package com.jme3.system.lwjgl; +import com.jme3.system.AppSettings; import com.jme3.system.JmeContext; /** @@ -41,4 +42,12 @@ public class LwjglOffscreenBuffer extends LwjglWindow { public LwjglOffscreenBuffer() { super(JmeContext.Type.OffscreenSurface); } + + @Override + protected void showWindow() { + } + + @Override + protected void setWindowIcon(final AppSettings settings) { + } } diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java index d5e515410..01eae0c16 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java @@ -32,6 +32,48 @@ package com.jme3.system.lwjgl; +import static org.lwjgl.glfw.GLFW.GLFW_ALPHA_BITS; +import static org.lwjgl.glfw.GLFW.GLFW_BLUE_BITS; +import static org.lwjgl.glfw.GLFW.GLFW_CONTEXT_VERSION_MAJOR; +import static org.lwjgl.glfw.GLFW.GLFW_CONTEXT_VERSION_MINOR; +import static org.lwjgl.glfw.GLFW.GLFW_DEPTH_BITS; +import static org.lwjgl.glfw.GLFW.GLFW_FALSE; +import static org.lwjgl.glfw.GLFW.GLFW_GREEN_BITS; +import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_CORE_PROFILE; +import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_DEBUG_CONTEXT; +import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_FORWARD_COMPAT; +import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_PROFILE; +import static org.lwjgl.glfw.GLFW.GLFW_RED_BITS; +import static org.lwjgl.glfw.GLFW.GLFW_REFRESH_RATE; +import static org.lwjgl.glfw.GLFW.GLFW_RESIZABLE; +import static org.lwjgl.glfw.GLFW.GLFW_SAMPLES; +import static org.lwjgl.glfw.GLFW.GLFW_SRGB_CAPABLE; +import static org.lwjgl.glfw.GLFW.GLFW_STENCIL_BITS; +import static org.lwjgl.glfw.GLFW.GLFW_STEREO; +import static org.lwjgl.glfw.GLFW.GLFW_TRUE; +import static org.lwjgl.glfw.GLFW.GLFW_VISIBLE; +import static org.lwjgl.glfw.GLFW.glfwCreateWindow; +import static org.lwjgl.glfw.GLFW.glfwDefaultWindowHints; +import static org.lwjgl.glfw.GLFW.glfwDestroyWindow; +import static org.lwjgl.glfw.GLFW.glfwGetPrimaryMonitor; +import static org.lwjgl.glfw.GLFW.glfwGetVideoMode; +import static org.lwjgl.glfw.GLFW.glfwInit; +import static org.lwjgl.glfw.GLFW.glfwMakeContextCurrent; +import static org.lwjgl.glfw.GLFW.glfwPollEvents; +import static org.lwjgl.glfw.GLFW.glfwSetErrorCallback; +import static org.lwjgl.glfw.GLFW.glfwSetWindowFocusCallback; +import static org.lwjgl.glfw.GLFW.glfwSetWindowIcon; +import static org.lwjgl.glfw.GLFW.glfwSetWindowPos; +import static org.lwjgl.glfw.GLFW.glfwSetWindowSizeCallback; +import static org.lwjgl.glfw.GLFW.glfwSetWindowTitle; +import static org.lwjgl.glfw.GLFW.glfwShowWindow; +import static org.lwjgl.glfw.GLFW.glfwSwapBuffers; +import static org.lwjgl.glfw.GLFW.glfwSwapInterval; +import static org.lwjgl.glfw.GLFW.glfwWindowHint; +import static org.lwjgl.glfw.GLFW.glfwWindowShouldClose; +import static org.lwjgl.opengl.GL11.GL_FALSE; +import static org.lwjgl.system.MemoryUtil.NULL; + import com.jme3.input.JoyInput; import com.jme3.input.KeyInput; import com.jme3.input.MouseInput; @@ -43,20 +85,21 @@ import com.jme3.system.AppSettings; import com.jme3.system.JmeContext; import com.jme3.system.JmeSystem; import com.jme3.system.NanoTimer; -import org.lwjgl.glfw.*; +import com.jme3.util.BufferUtils; -import java.awt.*; +import org.lwjgl.Version; +import org.lwjgl.glfw.GLFWErrorCallback; +import org.lwjgl.glfw.GLFWImage; +import org.lwjgl.glfw.GLFWVidMode; +import org.lwjgl.glfw.GLFWWindowFocusCallback; +import org.lwjgl.glfw.GLFWWindowSizeCallback; + +import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; -import org.lwjgl.Version; - -import static org.lwjgl.glfw.GLFW.*; -import static org.lwjgl.opengl.GL11.GL_FALSE; -import static org.lwjgl.opengl.GL11.GL_TRUE; -import static org.lwjgl.system.MemoryUtil.NULL; /** * A wrapper class over the GLFW framework in LWJGL 3. @@ -67,15 +110,10 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable { private static final Logger LOGGER = Logger.getLogger(LwjglWindow.class.getName()); - protected AtomicBoolean needClose = new AtomicBoolean(false); + protected final AtomicBoolean needClose = new AtomicBoolean(false); protected final AtomicBoolean needRestart = new AtomicBoolean(false); - protected boolean wasActive = false; - protected boolean autoFlush = true; - protected boolean allowSwapBuffers = false; - private long window = NULL; + private final JmeContext.Type type; - private int frameRateLimit = -1; - private double frameSleepTime; private GLFWErrorCallback errorCallback; private GLFWWindowSizeCallback windowSizeCallback; @@ -83,6 +121,14 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable { private Thread mainThread; + private double frameSleepTime; + private long window = NULL; + private int frameRateLimit = -1; + + protected boolean wasActive = false; + protected boolean autoFlush = true; + protected boolean allowSwapBuffers = false; + public LwjglWindow(final JmeContext.Type type) { if (!JmeContext.Type.Display.equals(type) && !JmeContext.Type.OffscreenSurface.equals(type) && !JmeContext.Type.Canvas.equals(type)) { throw new IllegalArgumentException("Unsupported type '" + type.name() + "' provided"); @@ -170,8 +216,6 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable { glfwWindowHint(GLFW_VISIBLE, GL_FALSE); glfwWindowHint(GLFW_RESIZABLE, settings.isResizable() ? GLFW_TRUE : GLFW_FALSE); - - //glfwWindowHint(GLFW_DOUBLE_BUFFER, GLFW_TRUE); glfwWindowHint(GLFW_DEPTH_BITS, settings.getDepthBits()); glfwWindowHint(GLFW_STENCIL_BITS, settings.getStencilBits()); glfwWindowHint(GLFW_SAMPLES, settings.getSamples()); @@ -271,12 +315,89 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable { glfwSwapInterval(0); } + setWindowIcon(settings); + showWindow(); + + allowSwapBuffers = settings.isSwapBuffers(); + } + protected void showWindow() { glfwShowWindow(window); + } - allowSwapBuffers = settings.isSwapBuffers(); + /** + * Set custom icons to the window of this application. + */ + protected void setWindowIcon(final AppSettings settings) { + + final Object[] icons = settings.getIcons(); + if (icons == null) return; + + final GLFWImage[] images = imagesToGLFWImages(icons); + + try (final GLFWImage.Buffer iconSet = GLFWImage.malloc(images.length)) { - // TODO: When GLFW 3.2 is released and included in LWJGL 3.x then we should hopefully be able to set the window icon. + for (int i = images.length - 1; i >= 0; i--) { + final GLFWImage image = images[i]; + iconSet.put(i, image); + } + + glfwSetWindowIcon(window, iconSet); + } + } + + /** + * Convert array of images to array of {@link GLFWImage}. + */ + private GLFWImage[] imagesToGLFWImages(final Object[] images) { + + final GLFWImage[] out = new GLFWImage[images.length]; + + for (int i = 0; i < images.length; i++) { + final BufferedImage image = (BufferedImage) images[i]; + out[i] = imageToGLFWImage(image); + } + + return out; + } + + /** + * Convert the {@link BufferedImage} to the {@link GLFWImage}. + */ + private GLFWImage imageToGLFWImage(BufferedImage image) { + + if (image.getType() != BufferedImage.TYPE_INT_ARGB_PRE) { + + final BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB_PRE); + final Graphics2D graphics = convertedImage.createGraphics(); + + final int targetWidth = image.getWidth(); + final int targetHeight = image.getHeight(); + + graphics.drawImage(image, 0, 0, targetWidth, targetHeight, null); + graphics.dispose(); + + image = convertedImage; + } + + final ByteBuffer buffer = BufferUtils.createByteBuffer(image.getWidth() * image.getHeight() * 4); + + for (int i = 0; i < image.getHeight(); i++) { + for (int j = 0; j < image.getWidth(); j++) { + int colorSpace = image.getRGB(j, i); + buffer.put((byte) ((colorSpace << 8) >> 24)); + buffer.put((byte) ((colorSpace << 16) >> 24)); + buffer.put((byte) ((colorSpace << 24) >> 24)); + buffer.put((byte) (colorSpace >> 24)); + } + } + + buffer.flip(); + + final GLFWImage result = GLFWImage.create(); + result.set(image.getWidth(), image.getHeight(), buffer); + + return result; } /** @@ -307,7 +428,7 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable { glfwDestroyWindow(window); window = NULL; } - } catch (Exception ex) { + } catch (final Exception ex) { listener.handleError("Failed to destroy context", ex); } } @@ -355,13 +476,13 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable { created.set(true); super.internalCreate(); - + //create OpenCL //Must be done here because the window handle is needed if (settings.isOpenCLSupport()) { initOpenCL(window); } - + } catch (Exception ex) { try { if (window != NULL) { @@ -543,44 +664,4 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable { public long getWindowHandle() { return window; } - - - // TODO: Implement support for window icon when GLFW supports it. - - private ByteBuffer[] imagesToByteBuffers(Object[] images) { - ByteBuffer[] out = new ByteBuffer[images.length]; - for (int i = 0; i < images.length; i++) { - BufferedImage image = (BufferedImage) images[i]; - out[i] = imageToByteBuffer(image); - } - return out; - } - - private ByteBuffer imageToByteBuffer(BufferedImage image) { - if (image.getType() != BufferedImage.TYPE_INT_ARGB_PRE) { - BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB_PRE); - Graphics2D g = convertedImage.createGraphics(); - double width = image.getWidth() * (double) 1; - double height = image.getHeight() * (double) 1; - g.drawImage(image, (int) ((convertedImage.getWidth() - width) / 2), - (int) ((convertedImage.getHeight() - height) / 2), - (int) (width), (int) (height), null); - g.dispose(); - image = convertedImage; - } - - byte[] imageBuffer = new byte[image.getWidth() * image.getHeight() * 4]; - int counter = 0; - for (int i = 0; i < image.getHeight(); i++) { - for (int j = 0; j < image.getWidth(); j++) { - int colorSpace = image.getRGB(j, i); - imageBuffer[counter + 0] = (byte) ((colorSpace << 8) >> 24); - imageBuffer[counter + 1] = (byte) ((colorSpace << 16) >> 24); - imageBuffer[counter + 2] = (byte) ((colorSpace << 24) >> 24); - imageBuffer[counter + 3] = (byte) (colorSpace >> 24); - counter += 4; - } - } - return ByteBuffer.wrap(imageBuffer); - } }