|
|
|
@ -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); |
|
|
|
|
|
|
|
|
|
// TODO: When GLFW 3.2 is released and included in LWJGL 3.x then we should hopefully be able to set the window icon.
|
|
|
|
|
try (final GLFWImage.Buffer iconSet = GLFWImage.malloc(images.length)) { |
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -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); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|