LWJGL3 improvements

* Added key remapping for GLFW key constants
 * Rename AppSettings.getGammaCorrection() to isGammaCorrection()
 * Use LWJGL3 artifacts from maven
 * Minor compatibility changes for LWJGL 3.0.0b
 * Fixed some minor bugs in LwjglWindow
experimental
Kirill Vainer 9 years ago
parent 6cb691592d
commit 2ca55c8b3a
  1. 9
      jme3-core/src/main/java/com/jme3/input/KeyInput.java
  2. 8
      jme3-core/src/main/java/com/jme3/input/KeyNames.java
  3. 2
      jme3-core/src/main/java/com/jme3/system/AppSettings.java
  4. 2
      jme3-desktop/src/main/java/com/jme3/app/SettingsDialog.java
  5. 5
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java
  6. 79
      jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java
  7. 12
      jme3-lwjgl3/build.gradle
  8. BIN
      jme3-lwjgl3/lib/lwjgl-3.0.0b-35-natives.jar
  9. BIN
      jme3-lwjgl3/lib/lwjgl-3.0.0b-35.jar
  10. 40
      jme3-lwjgl3/src/main/java/com/jme3/audio/lwjgl/LwjglALC.java
  11. 3
      jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwKeyInput.java
  12. 171
      jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwKeyMap.java
  13. 138
      jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwMouseInput.java
  14. 28
      jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java
  15. 129
      jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java

@ -36,6 +36,11 @@ package com.jme3.input;
*/ */
public interface KeyInput extends Input { public interface KeyInput extends Input {
/**
* unmapped key.
*/
public static final int KEY_UNKNOWN = 0x00;
/** /**
* escape key. * escape key.
*/ */
@ -539,4 +544,8 @@ public interface KeyInput extends Input {
*/ */
public static final int KEY_SLEEP = 0xDF; public static final int KEY_SLEEP = 0xDF;
/**
* the last key.
*/
public static final int KEY_LAST = 0xE0;
} }

@ -38,6 +38,7 @@ public class KeyNames {
private static final String[] KEY_NAMES = new String[0xFF]; private static final String[] KEY_NAMES = new String[0xFF];
static { static {
KEY_NAMES[KEY_UNKNOWN] = "Unknown";
KEY_NAMES[KEY_0] = "0"; KEY_NAMES[KEY_0] = "0";
KEY_NAMES[KEY_1] = "1"; KEY_NAMES[KEY_1] = "1";
KEY_NAMES[KEY_2] = "2"; KEY_NAMES[KEY_2] = "2";
@ -105,9 +106,10 @@ public class KeyNames {
KEY_NAMES[KEY_NUMPADEQUALS] = "Numpad ="; KEY_NAMES[KEY_NUMPADEQUALS] = "Numpad =";
KEY_NAMES[KEY_NUMPADENTER] = "Numpad Enter"; KEY_NAMES[KEY_NUMPADENTER] = "Numpad Enter";
KEY_NAMES[KEY_NUMPADCOMMA] = "Numpad ."; KEY_NAMES[KEY_NUMPADCOMMA] = "Numpad ,";
KEY_NAMES[KEY_DIVIDE] = "Numpad /"; KEY_NAMES[KEY_DIVIDE] = "Numpad /";
KEY_NAMES[KEY_SUBTRACT] = "Numpad -";
KEY_NAMES[KEY_DECIMAL] = "Numpad .";
KEY_NAMES[KEY_LMENU] = "Left Alt"; KEY_NAMES[KEY_LMENU] = "Left Alt";
KEY_NAMES[KEY_RMENU] = "Right Alt"; KEY_NAMES[KEY_RMENU] = "Right Alt";
@ -178,7 +180,7 @@ public class KeyNames {
KEY_NAMES[KEY_UNLABELED] = "Unlabeled"; KEY_NAMES[KEY_UNLABELED] = "Unlabeled";
} }
public String getName(int keyId){ public static String getName(int keyId) {
return KEY_NAMES[keyId]; return KEY_NAMES[keyId];
} }
} }

@ -963,7 +963,7 @@ public final class AppSettings extends HashMap<String, Object> {
return getString("SettingsDialogImage"); return getString("SettingsDialogImage");
} }
public boolean getGammaCorrection() { public boolean isGammaCorrection() {
return getBoolean("GammaCorrection"); return getBoolean("GammaCorrection");
} }

@ -360,7 +360,7 @@ public final class SettingsDialog extends JFrame {
vsyncBox.setSelected(source.isVSync()); vsyncBox.setSelected(source.isVSync());
gammaBox = new JCheckBox(resourceBundle.getString("checkbox.gamma")); gammaBox = new JCheckBox(resourceBundle.getString("checkbox.gamma"));
gammaBox.setSelected(source.getGammaCorrection()); gammaBox.setSelected(source.isGammaCorrection());
gbc = new GridBagConstraints(); gbc = new GridBagConstraints();
gbc.weightx = 0.5; gbc.weightx = 0.5;

@ -54,6 +54,7 @@ import com.jme3.system.AppSettings;
import com.jme3.system.JmeContext; import com.jme3.system.JmeContext;
import com.jme3.system.NanoTimer; import com.jme3.system.NanoTimer;
import com.jme3.system.NativeLibraryLoader; import com.jme3.system.NativeLibraryLoader;
import com.jme3.system.NullRenderer;
import com.jme3.system.SystemListener; import com.jme3.system.SystemListener;
import com.jme3.system.Timer; import com.jme3.system.Timer;
@ -202,8 +203,8 @@ public abstract class JoglContext implements JmeContext {
GLContext.getCurrent().addGLDebugListener(new JoglGLDebugOutputHandler()); GLContext.getCurrent().addGLDebugListener(new JoglGLDebugOutputHandler());
} }
renderer.setMainFrameBufferSrgb(settings.getGammaCorrection()); renderer.setMainFrameBufferSrgb(settings.isGammaCorrection());
renderer.setLinearizeSrgbImages(settings.getGammaCorrection()); renderer.setLinearizeSrgbImages(settings.isGammaCorrection());
// Init input // Init input
if (keyInput != null) { if (keyInput != null) {

@ -29,7 +29,6 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.jme3.system.lwjgl; package com.jme3.system.lwjgl;
import com.jme3.input.lwjgl.JInputJoyInput; import com.jme3.input.lwjgl.JInputJoyInput;
@ -53,6 +52,7 @@ import com.jme3.renderer.opengl.GLTiming;
import com.jme3.renderer.opengl.GLTimingState; import com.jme3.renderer.opengl.GLTimingState;
import com.jme3.renderer.opengl.GLTracer; import com.jme3.renderer.opengl.GLTracer;
import com.jme3.system.*; import com.jme3.system.*;
import java.io.File;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
@ -82,18 +82,18 @@ public abstract class LwjglContext implements JmeContext {
protected Timer timer; protected Timer timer;
protected SystemListener listener; protected SystemListener listener;
public void setSystemListener(SystemListener listener){ public void setSystemListener(SystemListener listener) {
this.listener = listener; this.listener = listener;
} }
protected void printContextInitInfo() { protected void printContextInitInfo() {
logger.log(Level.INFO, "LWJGL {0} context running on thread {1}\n" + logger.log(Level.INFO, "LWJGL {0} context running on thread {1}\n"
" * Graphics Adapter: {2}\n" + + " * Graphics Adapter: {2}\n"
" * Driver Version: {3}\n" + + " * Driver Version: {3}\n"
" * Scaling Factor: {4}", + " * Scaling Factor: {4}",
new Object[]{ Sys.getVersion(), Thread.currentThread().getName(), new Object[]{Sys.getVersion(), Thread.currentThread().getName(),
Display.getAdapter(), Display.getVersion(), Display.getAdapter(), Display.getVersion(),
Display.getPixelScaleFactor() }); Display.getPixelScaleFactor()});
} }
protected ContextAttribs createContextAttribs() { protected ContextAttribs createContextAttribs() {
@ -162,26 +162,33 @@ public abstract class LwjglContext implements JmeContext {
} }
} }
} }
protected void loadNatives() { protected void loadNatives() {
if (JmeSystem.isLowPermissions()) { if (JmeSystem.isLowPermissions()) {
return; return;
} }
String extractPath = NativeLibraryLoader.getExtractionFolder().getAbsolutePath();
if ("LWJGL".equals(settings.getAudioRenderer())) { if ("LWJGL".equals(settings.getAudioRenderer())) {
NativeLibraryLoader.loadNativeLibrary("openal", true); NativeLibraryLoader.loadNativeLibrary("openal", true);
} }
if (settings.useJoysticks()) { if (settings.useJoysticks()) {
NativeLibraryLoader.loadNativeLibrary("jinput", true); System.setProperty("net.java.games.input.librarypath", extractPath);
NativeLibraryLoader.loadNativeLibrary("jinput-dx8", true); NativeLibraryLoader.loadNativeLibrary("jinput", true, false);
NativeLibraryLoader.loadNativeLibrary("jinput-dx8", true, false);
} }
if (NativeLibraryLoader.isUsingNativeBullet()) { if (NativeLibraryLoader.isUsingNativeBullet()) {
NativeLibraryLoader.loadNativeLibrary("bulletjme", true); NativeLibraryLoader.loadNativeLibrary("bulletjme", true);
} }
NativeLibraryLoader.loadNativeLibrary("lwjgl", true);
System.setProperty("org.lwjgl.librarypath", extractPath);
NativeLibraryLoader.loadNativeLibrary("lwjgl", true, false);
} }
protected int getNumSamplesToUse() { protected int getNumSamplesToUse() {
int samples = 0; int samples = 0;
if (settings.getSamples() > 1){ if (settings.getSamples() > 1) {
samples = settings.getSamples(); samples = settings.getSamples();
int supportedSamples = determineMaxSamples(samples); int supportedSamples = determineMaxSamples(samples);
if (supportedSamples < samples) { if (supportedSamples < samples) {
@ -196,14 +203,14 @@ public abstract class LwjglContext implements JmeContext {
return samples; return samples;
} }
protected void initContextFirstTime(){ protected void initContextFirstTime() {
if (!GLContext.getCapabilities().OpenGL20) { if (!GLContext.getCapabilities().OpenGL20) {
throw new RendererException("OpenGL 2.0 or higher is " + throw new RendererException("OpenGL 2.0 or higher is "
"required for jMonkeyEngine"); + "required for jMonkeyEngine");
} }
if (settings.getRenderer().equals(AppSettings.LWJGL_OPENGL2) if (settings.getRenderer().equals(AppSettings.LWJGL_OPENGL2)
|| settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)) { || settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)) {
GL gl = new LwjglGL(); GL gl = new LwjglGL();
GLExt glext = new LwjglGLExt(); GLExt glext = new LwjglGLExt();
GLFbo glfbo; GLFbo glfbo;
@ -215,20 +222,20 @@ public abstract class LwjglContext implements JmeContext {
} }
if (settings.getBoolean("GraphicsDebug")) { if (settings.getBoolean("GraphicsDebug")) {
gl = new GLDebugDesktop(gl, glext, glfbo); gl = new GLDebugDesktop(gl, glext, glfbo);
glext = (GLExt) gl; glext = (GLExt) gl;
glfbo = (GLFbo) gl; glfbo = (GLFbo) gl;
} }
if (settings.getBoolean("GraphicsTiming")) { if (settings.getBoolean("GraphicsTiming")) {
GLTimingState timingState = new GLTimingState(); GLTimingState timingState = new GLTimingState();
gl = (GL) GLTiming.createGLTiming(gl, timingState, GL.class, GL2.class, GL3.class, GL4.class); gl = (GL) GLTiming.createGLTiming(gl, timingState, GL.class, GL2.class, GL3.class, GL4.class);
glext = (GLExt) GLTiming.createGLTiming(glext, timingState, GLExt.class); glext = (GLExt) GLTiming.createGLTiming(glext, timingState, GLExt.class);
glfbo = (GLFbo) GLTiming.createGLTiming(glfbo, timingState, GLFbo.class); glfbo = (GLFbo) GLTiming.createGLTiming(glfbo, timingState, GLFbo.class);
} }
if (settings.getBoolean("GraphicsTrace")) { if (settings.getBoolean("GraphicsTrace")) {
gl = (GL) GLTracer.createDesktopGlTracer(gl, GL.class, GL2.class, GL3.class, GL4.class); gl = (GL) GLTracer.createDesktopGlTracer(gl, GL.class, GL2.class, GL3.class, GL4.class);
glext = (GLExt) GLTracer.createDesktopGlTracer(glext, GLExt.class); glext = (GLExt) GLTracer.createDesktopGlTracer(glext, GLExt.class);
glfbo = (GLFbo) GLTracer.createDesktopGlTracer(glfbo, GLFbo.class); glfbo = (GLFbo) GLTracer.createDesktopGlTracer(glfbo, GLFbo.class);
} }
@ -243,8 +250,8 @@ public abstract class LwjglContext implements JmeContext {
ARBDebugOutput.glDebugMessageCallbackARB(new ARBDebugOutputCallback(new LwjglGLDebugOutputHandler())); ARBDebugOutput.glDebugMessageCallbackARB(new ARBDebugOutputCallback(new LwjglGLDebugOutputHandler()));
} }
renderer.setMainFrameBufferSrgb(settings.getGammaCorrection()); renderer.setMainFrameBufferSrgb(settings.isGammaCorrection());
renderer.setLinearizeSrgbImages(settings.getGammaCorrection()); renderer.setLinearizeSrgbImages(settings.isGammaCorrection());
// Init input // Init input
if (keyInput != null) { if (keyInput != null) {
@ -260,42 +267,42 @@ public abstract class LwjglContext implements JmeContext {
} }
} }
public void internalDestroy(){ public void internalDestroy() {
renderer = null; renderer = null;
timer = null; timer = null;
renderable.set(false); renderable.set(false);
synchronized (createdLock){ synchronized (createdLock) {
created.set(false); created.set(false);
createdLock.notifyAll(); createdLock.notifyAll();
} }
} }
public void internalCreate(){ public void internalCreate() {
timer = new LwjglTimer(); timer = new LwjglTimer();
synchronized (createdLock){ synchronized (createdLock) {
created.set(true); created.set(true);
createdLock.notifyAll(); createdLock.notifyAll();
} }
if (renderable.get()){ if (renderable.get()) {
initContextFirstTime(); initContextFirstTime();
}else{ } else {
assert getType() == Type.Canvas; assert getType() == Type.Canvas;
} }
} }
public void create(){ public void create() {
create(false); create(false);
} }
public void destroy(){ public void destroy() {
destroy(false); destroy(false);
} }
protected void waitFor(boolean createdVal){ protected void waitFor(boolean createdVal) {
synchronized (createdLock){ synchronized (createdLock) {
while (created.get() != createdVal){ while (created.get() != createdVal) {
try { try {
createdLock.wait(); createdLock.wait();
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
@ -304,11 +311,11 @@ public abstract class LwjglContext implements JmeContext {
} }
} }
public boolean isCreated(){ public boolean isCreated() {
return created.get(); return created.get();
} }
public boolean isRenderable(){ public boolean isRenderable() {
return renderable.get(); return renderable.get();
} }
@ -316,7 +323,7 @@ public abstract class LwjglContext implements JmeContext {
this.settings.copyFrom(settings); this.settings.copyFrom(settings);
} }
public AppSettings getSettings(){ public AppSettings getSettings() {
return settings; return settings;
} }

@ -2,14 +2,14 @@ if (!hasProperty('mainClass')) {
ext.mainClass = '' ext.mainClass = ''
} }
repositories { def lwjglVersion = '3.0.0b'
maven {
url "https://oss.sonatype.org/content/repositories/snapshots"
}
}
dependencies { dependencies {
compile project(':jme3-core') compile project(':jme3-core')
compile project(':jme3-desktop') compile project(':jme3-desktop')
compile files('lib/lwjgl-3.0.0b-35.jar', 'lib/lwjgl-3.0.0b-35-natives.jar')
compile "org.lwjgl:lwjgl:${lwjglVersion}"
compile "org.lwjgl:lwjgl-platform:${lwjglVersion}:natives-windows"
compile "org.lwjgl:lwjgl-platform:${lwjglVersion}:natives-linux"
compile "org.lwjgl:lwjgl-platform:${lwjglVersion}:natives-osx"
} }

@ -32,32 +32,38 @@
package com.jme3.audio.lwjgl; package com.jme3.audio.lwjgl;
import com.jme3.audio.openal.ALC; import com.jme3.audio.openal.ALC;
import java.nio.IntBuffer;
import org.lwjgl.openal.ALC10; import org.lwjgl.openal.ALC10;
import org.lwjgl.openal.ALContext; import org.lwjgl.openal.ALContext;
import org.lwjgl.openal.ALDevice; import org.lwjgl.openal.ALDevice;
import org.lwjgl.openal.SOFTPauseDevice;
import java.nio.IntBuffer;
import static org.lwjgl.openal.ALC10.alcGetContextsDevice;
import static org.lwjgl.openal.ALC10.alcGetCurrentContext;
public class LwjglALC implements ALC { public class LwjglALC implements ALC {
private ALDevice device; private ALDevice device;
private ALContext context; private ALContext context;
private long contextId;
private long deviceId;
public void createALC() { public void createALC() {
device = ALDevice.create(); device = ALDevice.create();
context = ALContext.create(device); context = ALContext.create(device);
context.makeCurrent();
contextId = ALC10.alcGetCurrentContext();
deviceId = ALC10.alcGetContextsDevice(contextId);
} }
public void destroyALC() { public void destroyALC() {
if (context != null) { if (context != null) {
context.destroy(); context.destroy();
context = null;
} }
if (device != null) { if (device != null) {
device.destroy(); device.destroy();
device = null;
} }
} }
@ -66,31 +72,29 @@ public class LwjglALC implements ALC {
} }
public String alcGetString(final int parameter) { public String alcGetString(final int parameter) {
final long context = alcGetCurrentContext(); return ALC10.alcGetString(deviceId, parameter);
final long device = alcGetContextsDevice(context);
return ALC10.alcGetString(device, parameter);
} }
public boolean alcIsExtensionPresent(final String extension) { public boolean alcIsExtensionPresent(final String extension) {
final long context = alcGetCurrentContext(); return ALC10.alcIsExtensionPresent(deviceId, extension);
final long device = alcGetContextsDevice(context);
return ALC10.alcIsExtensionPresent(device, extension);
} }
public void alcGetInteger(final int param, final IntBuffer buffer, final int size) { public void alcGetInteger(final int param, final IntBuffer buffer, final int size) {
if (buffer.position() != 0) throw new AssertionError(); if (buffer.position() != 0) {
if (buffer.limit() != size) throw new AssertionError(); throw new AssertionError();
}
final long context = alcGetCurrentContext(); if (buffer.limit() != size) {
final long device = alcGetContextsDevice(context); throw new AssertionError();
final int value = ALC10.alcGetInteger(device, param); }
//buffer.put(value); ALC10.alcGetIntegerv(deviceId, param, buffer);
} }
public void alcDevicePauseSOFT() { public void alcDevicePauseSOFT() {
SOFTPauseDevice.alcDevicePauseSOFT(deviceId);
} }
public void alcDeviceResumeSOFT() { public void alcDeviceResumeSOFT() {
SOFTPauseDevice.alcDeviceResumeSOFT(deviceId);
} }
} }

@ -66,7 +66,8 @@ public class GlfwKeyInput implements KeyInput {
glfwSetKeyCallback(context.getWindowHandle(), keyCallback = new GLFWKeyCallback() { glfwSetKeyCallback(context.getWindowHandle(), keyCallback = new GLFWKeyCallback() {
@Override @Override
public void invoke(long window, int key, int scancode, int action, int mods) { public void invoke(long window, int key, int scancode, int action, int mods) {
final KeyInputEvent evt = new KeyInputEvent(scancode, (char) key, GLFW_PRESS == action, GLFW_REPEAT == action); int jmeKey = GlfwKeyMap.toJmeKeyCode(key);
final KeyInputEvent evt = new KeyInputEvent(jmeKey, (char) key, GLFW_PRESS == action, GLFW_REPEAT == action);
evt.setTime(getInputTimeNanos()); evt.setTime(getInputTimeNanos());
keyInputEvents.add(evt); keyInputEvents.add(evt);
} }

@ -0,0 +1,171 @@
/*
* Copyright (c) 2009-2015 jMonkeyEngine
* All rights reserved.
*
* 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 'jMonkeyEngine' 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) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.input.lwjgl;
import static org.lwjgl.glfw.GLFW.*;
import static com.jme3.input.KeyInput.*;
public class GlfwKeyMap {
private static final int[] glfwToJmeKeyMap = new int[GLFW_KEY_LAST + 1];
private static void reg(int jmeKey, int glfwKey) {
glfwToJmeKeyMap[glfwKey] = jmeKey;
}
static {
reg(KEY_ESCAPE, GLFW_KEY_ESCAPE);
reg(KEY_1, GLFW_KEY_1);
reg(KEY_2, GLFW_KEY_2);
reg(KEY_3, GLFW_KEY_3);
reg(KEY_4, GLFW_KEY_4);
reg(KEY_5, GLFW_KEY_5);
reg(KEY_6, GLFW_KEY_6);
reg(KEY_7, GLFW_KEY_7);
reg(KEY_8, GLFW_KEY_8);
reg(KEY_9, GLFW_KEY_9);
reg(KEY_0, GLFW_KEY_0);
reg(KEY_MINUS, GLFW_KEY_MINUS);
reg(KEY_EQUALS, GLFW_KEY_EQUAL);
reg(KEY_BACK, GLFW_KEY_BACKSPACE);
reg(KEY_TAB, GLFW_KEY_TAB);
reg(KEY_Q, GLFW_KEY_Q);
reg(KEY_W, GLFW_KEY_W);
reg(KEY_E, GLFW_KEY_E);
reg(KEY_R, GLFW_KEY_R);
reg(KEY_T, GLFW_KEY_T);
reg(KEY_Y, GLFW_KEY_Y);
reg(KEY_U, GLFW_KEY_U);
reg(KEY_I, GLFW_KEY_I);
reg(KEY_O, GLFW_KEY_O);
reg(KEY_P, GLFW_KEY_P);
reg(KEY_LBRACKET, GLFW_KEY_LEFT_BRACKET);
reg(KEY_RBRACKET, GLFW_KEY_RIGHT_BRACKET);
reg(KEY_RETURN, GLFW_KEY_ENTER);
reg(KEY_LCONTROL, GLFW_KEY_LEFT_CONTROL);
reg(KEY_A, GLFW_KEY_A);
reg(KEY_S, GLFW_KEY_S);
reg(KEY_D, GLFW_KEY_D);
reg(KEY_F, GLFW_KEY_F);
reg(KEY_G, GLFW_KEY_G);
reg(KEY_H, GLFW_KEY_H);
reg(KEY_J, GLFW_KEY_J);
reg(KEY_K, GLFW_KEY_K);
reg(KEY_L, GLFW_KEY_L);
reg(KEY_SEMICOLON, GLFW_KEY_SEMICOLON);
reg(KEY_APOSTROPHE, GLFW_KEY_APOSTROPHE);
reg(KEY_GRAVE, GLFW_KEY_GRAVE_ACCENT);
reg(KEY_LSHIFT, GLFW_KEY_LEFT_SHIFT);
reg(KEY_BACKSLASH, GLFW_KEY_BACKSLASH);
reg(KEY_Z, GLFW_KEY_Z);
reg(KEY_X, GLFW_KEY_X);
reg(KEY_C, GLFW_KEY_C);
reg(KEY_V, GLFW_KEY_V);
reg(KEY_B, GLFW_KEY_B);
reg(KEY_N, GLFW_KEY_N);
reg(KEY_M, GLFW_KEY_M);
reg(KEY_COMMA, GLFW_KEY_COMMA);
reg(KEY_PERIOD, GLFW_KEY_PERIOD);
reg(KEY_SLASH, GLFW_KEY_SLASH);
reg(KEY_RSHIFT, GLFW_KEY_RIGHT_SHIFT);
reg(KEY_MULTIPLY, GLFW_KEY_KP_MULTIPLY);
reg(KEY_LMENU, GLFW_KEY_LEFT_ALT);
reg(KEY_SPACE, GLFW_KEY_SPACE);
reg(KEY_CAPITAL, GLFW_KEY_CAPS_LOCK);
reg(KEY_F1, GLFW_KEY_F1);
reg(KEY_F2, GLFW_KEY_F2);
reg(KEY_F3, GLFW_KEY_F3);
reg(KEY_F4, GLFW_KEY_F4);
reg(KEY_F5, GLFW_KEY_F5);
reg(KEY_F6, GLFW_KEY_F6);
reg(KEY_F7, GLFW_KEY_F7);
reg(KEY_F8, GLFW_KEY_F8);
reg(KEY_F9, GLFW_KEY_F9);
reg(KEY_F10, GLFW_KEY_F10);
reg(KEY_NUMLOCK, GLFW_KEY_NUM_LOCK);
reg(KEY_SCROLL, GLFW_KEY_SCROLL_LOCK);
reg(KEY_NUMPAD7, GLFW_KEY_KP_7);
reg(KEY_NUMPAD8, GLFW_KEY_KP_8);
reg(KEY_NUMPAD9, GLFW_KEY_KP_9);
reg(KEY_SUBTRACT, GLFW_KEY_KP_SUBTRACT);
reg(KEY_NUMPAD4, GLFW_KEY_KP_4);
reg(KEY_NUMPAD5, GLFW_KEY_KP_5);
reg(KEY_NUMPAD6, GLFW_KEY_KP_6);
reg(KEY_ADD, GLFW_KEY_KP_ADD);
reg(KEY_NUMPAD1, GLFW_KEY_KP_1);
reg(KEY_NUMPAD2, GLFW_KEY_KP_2);
reg(KEY_NUMPAD3, GLFW_KEY_KP_3);
reg(KEY_NUMPAD0, GLFW_KEY_KP_0);
reg(KEY_DECIMAL, GLFW_KEY_KP_DECIMAL);
reg(KEY_F11, GLFW_KEY_F11);
reg(KEY_F12, GLFW_KEY_F12);
reg(KEY_F13, GLFW_KEY_F13);
reg(KEY_F14, GLFW_KEY_F14);
reg(KEY_F15, GLFW_KEY_F15);
//reg(KEY_KANA, GLFW_KEY_);
//reg(KEY_CONVERT, GLFW_KEY_);
//reg(KEY_NOCONVERT, GLFW_KEY_);
//reg(KEY_YEN, GLFW_KEY_);
//reg(KEY_NUMPADEQUALS, GLFW_KEY_);
//reg(KEY_CIRCUMFLEX, GLFW_KEY_);
//reg(KEY_AT, GLFW_KEY_);
//reg(KEY_COLON, GLFW_KEY_);
//reg(KEY_UNDERLINE, GLFW_KEY_);
//reg(KEY_KANJI, GLFW_KEY_);
//reg(KEY_STOP, GLFW_KEY_);
//reg(KEY_AX, GLFW_KEY_);
//reg(KEY_UNLABELED, GLFW_KEY_);
reg(KEY_NUMPADENTER, GLFW_KEY_KP_ENTER);
reg(KEY_RCONTROL, GLFW_KEY_RIGHT_CONTROL);
//reg(KEY_NUMPADCOMMA, GLFW_KEY_);
reg(KEY_DIVIDE, GLFW_KEY_KP_DIVIDE);
reg(KEY_SYSRQ, GLFW_KEY_PRINT_SCREEN);
reg(KEY_RMENU, GLFW_KEY_RIGHT_ALT);
reg(KEY_PAUSE, GLFW_KEY_PAUSE);
reg(KEY_HOME, GLFW_KEY_HOME);
reg(KEY_UP, GLFW_KEY_UP);
reg(KEY_PRIOR, GLFW_KEY_PAGE_UP);
reg(KEY_LEFT, GLFW_KEY_LEFT);
reg(KEY_RIGHT, GLFW_KEY_RIGHT);
reg(KEY_END, GLFW_KEY_END);
reg(KEY_DOWN, GLFW_KEY_DOWN);
reg(KEY_NEXT, GLFW_KEY_PAGE_DOWN);
reg(KEY_INSERT, GLFW_KEY_INSERT);
reg(KEY_DELETE, GLFW_KEY_DELETE);
reg(KEY_LMETA, GLFW_KEY_LEFT_SUPER);
reg(KEY_RMETA, GLFW_KEY_RIGHT_SUPER);
}
public static int toJmeKeyCode(int glfwKey) {
return glfwToJmeKeyMap[glfwKey];
}
}

@ -38,16 +38,22 @@ import com.jme3.input.RawInputListener;
import com.jme3.input.event.MouseButtonEvent; import com.jme3.input.event.MouseButtonEvent;
import com.jme3.input.event.MouseMotionEvent; import com.jme3.input.event.MouseMotionEvent;
import com.jme3.system.lwjgl.LwjglWindow; import com.jme3.system.lwjgl.LwjglWindow;
import com.jme3.util.BufferUtils;
import org.lwjgl.glfw.GLFWCursorPosCallback; import org.lwjgl.glfw.GLFWCursorPosCallback;
import org.lwjgl.glfw.GLFWMouseButtonCallback; import org.lwjgl.glfw.GLFWMouseButtonCallback;
import org.lwjgl.glfw.GLFWScrollCallback; import org.lwjgl.glfw.GLFWScrollCallback;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Map;
import java.util.Queue; import java.util.Queue;
import java.util.logging.Logger; import java.util.logging.Logger;
import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.glfw.GLFW.*;
import org.lwjgl.glfw.GLFWImage;
import org.lwjgl.system.MemoryUtil;
/** /**
* Captures mouse input using GLFW callbacks. It then temporarily stores these in event queues which are processed in the * Captures mouse input using GLFW callbacks. It then temporarily stores these in event queues which are processed in the
@ -74,57 +80,70 @@ public class GlfwMouseInput implements MouseInput {
private Queue<MouseMotionEvent> mouseMotionEvents = new LinkedList<MouseMotionEvent>(); private Queue<MouseMotionEvent> mouseMotionEvents = new LinkedList<MouseMotionEvent>();
private Queue<MouseButtonEvent> mouseButtonEvents = new LinkedList<MouseButtonEvent>(); private Queue<MouseButtonEvent> mouseButtonEvents = new LinkedList<MouseButtonEvent>();
public GlfwMouseInput(final LwjglWindow context) { private Map<JmeCursor, Long> jmeToGlfwCursorMap = new HashMap<JmeCursor, Long>();
public GlfwMouseInput(LwjglWindow context) {
this.context = context; this.context = context;
} }
private void onCursorPos(long window, double xpos, double ypos) {
int xDelta;
int yDelta;
int x = (int) Math.round(xpos);
int y = context.getSettings().getHeight() - (int) Math.round(ypos);
if (mouseX == 0) {
mouseX = x;
}
if (mouseY == 0) {
mouseY = y;
}
xDelta = x - mouseX;
yDelta = y - mouseY;
mouseX = x;
mouseY = y;
if (xDelta != 0 || yDelta != 0) {
final MouseMotionEvent mouseMotionEvent = new MouseMotionEvent(x, y, xDelta, yDelta, mouseWheel, 0);
mouseMotionEvent.setTime(getInputTimeNanos());
mouseMotionEvents.add(mouseMotionEvent);
}
}
private void onWheelScroll(long window, double xOffset, double yOffset) {
mouseWheel += yOffset;
final MouseMotionEvent mouseMotionEvent = new MouseMotionEvent(mouseX, mouseY, 0, 0, mouseWheel, (int) Math.round(yOffset));
mouseMotionEvent.setTime(getInputTimeNanos());
mouseMotionEvents.add(mouseMotionEvent);
}
private void onMouseButton(final long window, final int button, final int action, final int mods) {
final MouseButtonEvent mouseButtonEvent = new MouseButtonEvent(convertButton(button), action == GLFW_PRESS, mouseX, mouseY);
mouseButtonEvent.setTime(getInputTimeNanos());
mouseButtonEvents.add(mouseButtonEvent);
}
public void initialize() { public void initialize() {
glfwSetCursorPosCallback(context.getWindowHandle(), cursorPosCallback = new GLFWCursorPosCallback() { glfwSetCursorPosCallback(context.getWindowHandle(), cursorPosCallback = new GLFWCursorPosCallback() {
@Override @Override
public void invoke(long window, double xpos, double ypos) { public void invoke(long window, double xpos, double ypos) {
int xDelta; onCursorPos(window, xpos, ypos);
int yDelta;
int x = (int) Math.round(xpos);
int y = context.getSettings().getHeight() - (int) Math.round(ypos);
if (mouseX == 0) {
mouseX = x;
}
if (mouseY == 0) {
mouseY = y;
}
xDelta = x - mouseX;
yDelta = y - mouseY;
mouseX = x;
mouseY = y;
if (xDelta != 0 || yDelta != 0) {
final MouseMotionEvent mouseMotionEvent = new MouseMotionEvent(x, y, xDelta, yDelta, mouseWheel, 0);
mouseMotionEvent.setTime(getInputTimeNanos());
mouseMotionEvents.add(mouseMotionEvent);
}
} }
}); });
glfwSetScrollCallback(context.getWindowHandle(), scrollCallback = new GLFWScrollCallback() { glfwSetScrollCallback(context.getWindowHandle(), scrollCallback = new GLFWScrollCallback() {
@Override @Override
public void invoke(final long window, final double xOffset, final double yOffset) { public void invoke(final long window, final double xOffset, final double yOffset) {
mouseWheel += yOffset; onWheelScroll(window, xOffset, yOffset);
final MouseMotionEvent mouseMotionEvent = new MouseMotionEvent(mouseX, mouseY, 0, 0, mouseWheel, (int) Math.round(yOffset));
mouseMotionEvent.setTime(getInputTimeNanos());
mouseMotionEvents.add(mouseMotionEvent);
} }
}); });
glfwSetMouseButtonCallback(context.getWindowHandle(), mouseButtonCallback = new GLFWMouseButtonCallback() { glfwSetMouseButtonCallback(context.getWindowHandle(), mouseButtonCallback = new GLFWMouseButtonCallback() {
@Override @Override
public void invoke(final long window, final int button, final int action, final int mods) { public void invoke(final long window, final int button, final int action, final int mods) {
final MouseButtonEvent mouseButtonEvent = new MouseButtonEvent(convertButton(button), action == GLFW_PRESS, mouseX, mouseY); onMouseButton(window, button, action, mods);
mouseButtonEvent.setTime(getInputTimeNanos());
mouseButtonEvents.add(mouseButtonEvent);
} }
}); });
@ -160,6 +179,10 @@ public class GlfwMouseInput implements MouseInput {
scrollCallback.release(); scrollCallback.release();
mouseButtonCallback.release(); mouseButtonCallback.release();
for (long glfwCursor : jmeToGlfwCursorMap.values()) {
glfwDestroyCursor(glfwCursor);
}
logger.fine("Mouse destroyed."); logger.fine("Mouse destroyed.");
} }
@ -185,31 +208,52 @@ public class GlfwMouseInput implements MouseInput {
return (long) (glfwGetTime() * 1000000000); return (long) (glfwGetTime() * 1000000000);
} }
public void setNativeCursor(final JmeCursor jmeCursor) { private long createGlfwCursor(JmeCursor jmeCursor) {
GLFWImage glfwImage = new GLFWImage(BufferUtils.createByteBuffer(GLFWImage.SIZEOF));
// TODO: currently animated cursors are not supported
IntBuffer imageData = jmeCursor.getImagesData();
ByteBuffer buf = BufferUtils.createByteBuffer(imageData.capacity());
buf.asIntBuffer().put(imageData);
glfwImage.set(jmeCursor.getWidth(), jmeCursor.getHeight(), buf);
return glfwCreateCursor(glfwImage, jmeCursor.getXHotSpot(), jmeCursor.getYHotSpot());
}
public void setNativeCursor(JmeCursor jmeCursor) {
if (jmeCursor != null) { if (jmeCursor != null) {
final ByteBuffer byteBuffer = org.lwjgl.BufferUtils.createByteBuffer(jmeCursor.getImagesData().capacity()); Long glfwCursor = jmeToGlfwCursorMap.get(jmeCursor);
byteBuffer.asIntBuffer().put(jmeCursor.getImagesData().array());
final long cursor = glfwCreateCursor(byteBuffer, jmeCursor.getXHotSpot(), jmeCursor.getYHotSpot()); if (glfwCursor == null) {
glfwSetCursor(context.getWindowHandle(), cursor); glfwCursor = createGlfwCursor(jmeCursor);
jmeToGlfwCursorMap.put(jmeCursor, glfwCursor);
}
glfwSetCursor(context.getWindowHandle(), glfwCursor);
} else {
glfwSetCursor(context.getWindowHandle(), MemoryUtil.NULL);
} }
} }
/** /**
* Simply converts the GLFW button code to a JME button code. If there is no match it just returns the GLFW button * Simply converts the GLFW button code to a JME button code. If there is no
* code. Bare in mind GLFW supports 8 different mouse buttons. * match it just returns the GLFW button code. Bear in mind GLFW supports 8
* different mouse buttons.
* *
* @param glfwButton the raw GLFW button index. * @param glfwButton the raw GLFW button index.
* @return the mapped {@link MouseInput} button id. * @return the mapped {@link MouseInput} button id.
*/ */
private int convertButton(final int glfwButton) { private int convertButton(final int glfwButton) {
if (glfwButton == GLFW_MOUSE_BUTTON_LEFT) { switch (glfwButton) {
return MouseInput.BUTTON_LEFT; case GLFW_MOUSE_BUTTON_LEFT:
} else if(glfwButton == GLFW_MOUSE_BUTTON_MIDDLE) { return MouseInput.BUTTON_LEFT;
return MouseInput.BUTTON_MIDDLE; case GLFW_MOUSE_BUTTON_MIDDLE:
} else if(glfwButton == GLFW_MOUSE_BUTTON_RIGHT) { return MouseInput.BUTTON_MIDDLE;
return MouseInput.BUTTON_RIGHT; case GLFW_MOUSE_BUTTON_RIGHT:
return MouseInput.BUTTON_RIGHT;
default:
return glfwButton;
} }
return glfwButton;
} }
} }

@ -43,9 +43,7 @@ import com.jme3.renderer.lwjgl.LwjglGLFboEXT;
import com.jme3.renderer.lwjgl.LwjglGLFboGL3; import com.jme3.renderer.lwjgl.LwjglGLFboGL3;
import com.jme3.renderer.opengl.*; import com.jme3.renderer.opengl.*;
import com.jme3.system.*; import com.jme3.system.*;
import org.lwjgl.Sys;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.ARBDebugOutput;
import org.lwjgl.opengl.ARBFramebufferObject; import org.lwjgl.opengl.ARBFramebufferObject;
import org.lwjgl.opengl.EXTFramebufferMultisample; import org.lwjgl.opengl.EXTFramebufferMultisample;
import org.lwjgl.opengl.GLCapabilities; import org.lwjgl.opengl.GLCapabilities;
@ -53,9 +51,10 @@ import org.lwjgl.opengl.GLCapabilities;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import static org.lwjgl.glfw.GLFW.GLFW_TRUE;
import org.lwjgl.opengl.ARBDebugOutput;
import static org.lwjgl.opengl.GL.createCapabilities; import static org.lwjgl.opengl.GL.createCapabilities;
import static org.lwjgl.opengl.GL11.GL_TRUE;
import static org.lwjgl.opengl.GL11.glGetInteger; import static org.lwjgl.opengl.GL11.glGetInteger;
/** /**
@ -84,16 +83,16 @@ public abstract class LwjglContext implements JmeContext {
} }
protected void printContextInitInfo() { protected void printContextInitInfo() {
logger.log(Level.INFO, "LWJGL {0} context running on thread {1}\n" + logger.log(Level.INFO, "LWJGL {0} context running on thread {1}\n"
" * Graphics Adapter: GLFW {2}", + " * Graphics Adapter: GLFW {2}",
new Object[]{Sys.getVersion(), Thread.currentThread().getName(), GLFW.glfwGetVersionString()}); new Object[]{org.lwjgl.Version.getVersion(), Thread.currentThread().getName(), GLFW.glfwGetVersionString()});
} }
protected int determineMaxSamples() { protected int determineMaxSamples() {
// If we already have a valid context, determine samples using current context. // If we already have a valid context, determine samples using current context.
if (GLFW.glfwExtensionSupported("GL_ARB_framebuffer_object") == GL_TRUE) { if (GLFW.glfwExtensionSupported("GL_ARB_framebuffer_object") == GLFW_TRUE) {
return glGetInteger(ARBFramebufferObject.GL_MAX_SAMPLES); return glGetInteger(ARBFramebufferObject.GL_MAX_SAMPLES);
} else if (GLFW.glfwExtensionSupported("GL_EXT_framebuffer_multisample") == GL_TRUE) { } else if (GLFW.glfwExtensionSupported("GL_EXT_framebuffer_multisample") == GLFW_TRUE) {
return glGetInteger(EXTFramebufferMultisample.GL_MAX_SAMPLES_EXT); return glGetInteger(EXTFramebufferMultisample.GL_MAX_SAMPLES_EXT);
} }
@ -180,11 +179,11 @@ public abstract class LwjglContext implements JmeContext {
} }
if (capabilities.GL_ARB_debug_output && settings.getBoolean("GraphicsDebug")) { if (capabilities.GL_ARB_debug_output && settings.getBoolean("GraphicsDebug")) {
ARBDebugOutput.glDebugMessageCallbackARB(new LwjglGLDebugOutputHandler(), 0); // User param is zero. Not sure what we could use that for. ARBDebugOutput.glDebugMessageCallbackARB(new LwjglGLDebugOutputHandler(), 0);
} }
renderer.setMainFrameBufferSrgb(settings.getGammaCorrection()); renderer.setMainFrameBufferSrgb(settings.isGammaCorrection());
renderer.setLinearizeSrgbImages(settings.getGammaCorrection()); renderer.setLinearizeSrgbImages(settings.isGammaCorrection());
// Init input // Init input
if (keyInput != null) { if (keyInput != null) {
@ -198,7 +197,6 @@ public abstract class LwjglContext implements JmeContext {
if (joyInput != null) { if (joyInput != null) {
joyInput.initialize(); joyInput.initialize();
} }
renderable.set(true); renderable.set(true);
} }
@ -240,26 +238,32 @@ public abstract class LwjglContext implements JmeContext {
} }
} }
@Override
public boolean isCreated() { public boolean isCreated() {
return created.get(); return created.get();
} }
@Override
public boolean isRenderable() { public boolean isRenderable() {
return renderable.get(); return renderable.get();
} }
@Override
public void setSettings(AppSettings settings) { public void setSettings(AppSettings settings) {
this.settings.copyFrom(settings); this.settings.copyFrom(settings);
} }
@Override
public AppSettings getSettings() { public AppSettings getSettings() {
return settings; return settings;
} }
@Override
public Renderer getRenderer() { public Renderer getRenderer() {
return renderer; return renderer;
} }
@Override
public Timer getTimer() { public Timer getTimer() {
return timer; return timer;
} }

@ -43,7 +43,6 @@ import com.jme3.system.AppSettings;
import com.jme3.system.JmeContext; import com.jme3.system.JmeContext;
import com.jme3.system.JmeSystem; import com.jme3.system.JmeSystem;
import com.jme3.system.NanoTimer; import com.jme3.system.NanoTimer;
import org.lwjgl.Sys;
import org.lwjgl.glfw.*; import org.lwjgl.glfw.*;
import java.awt.*; import java.awt.*;
@ -52,6 +51,7 @@ import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.lwjgl.Version;
import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.GL_FALSE; import static org.lwjgl.opengl.GL11.GL_FALSE;
@ -72,7 +72,7 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
protected boolean wasActive = false; protected boolean wasActive = false;
protected boolean autoFlush = true; protected boolean autoFlush = true;
protected boolean allowSwapBuffers = false; protected boolean allowSwapBuffers = false;
private long window = -1; private long window = NULL;
private final JmeContext.Type type; private final JmeContext.Type type;
private int frameRateLimit = -1; private int frameRateLimit = -1;
private double frameSleepTime; private double frameSleepTime;
@ -102,7 +102,7 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
* @param title the title to set * @param title the title to set
*/ */
public void setTitle(final String title) { public void setTitle(final String title) {
if (created.get() && window != -1) { if (created.get() && window != NULL) {
glfwSetWindowTitle(window, title); glfwSetWindowTitle(window, title);
} }
} }
@ -127,45 +127,45 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
glfwSetErrorCallback(errorCallback = new GLFWErrorCallback() { glfwSetErrorCallback(errorCallback = new GLFWErrorCallback() {
@Override @Override
public void invoke(int error, long description) { public void invoke(int error, long description) {
final String message = Callbacks.errorCallbackDescriptionString(description); final String message = GLFWErrorCallback.getDescription(description);
listener.handleError(message, new Exception(message)); listener.handleError(message, new Exception(message));
} }
}); });
if (glfwInit() != GL_TRUE) { if (glfwInit() != GLFW_TRUE) {
throw new IllegalStateException("Unable to initialize GLFW"); throw new IllegalStateException("Unable to initialize GLFW");
} }
glfwDefaultWindowHints(); glfwDefaultWindowHints();
glfwWindowHint(GLFW_VISIBLE, GL_FALSE);
// TODO: Add support for monitor selection if (settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)) {
long monitor = NULL; glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
if (settings.isFullscreen()) { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
monitor = glfwGetPrimaryMonitor(); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
} else {
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
} }
final ByteBuffer videoMode = glfwGetVideoMode(glfwGetPrimaryMonitor()); if (settings.getBoolean("RendererDebug")) {
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
if (settings.getWidth() <= 0 || settings.getHeight() <= 0) {
settings.setResolution(GLFWvidmode.width(videoMode), GLFWvidmode.height(videoMode));
} }
window = glfwCreateWindow(settings.getWidth(), settings.getHeight(), settings.getTitle(), monitor, NULL); if (settings.isGammaCorrection()) {
glfwWindowHint(GLFW_SRGB_CAPABLE, GLFW_TRUE);
if (window == NULL) {
throw new RuntimeException("Failed to create the GLFW window");
} }
glfwWindowHint(GLFW_RESIZABLE, settings.isResizable() ? GL_TRUE : GL_FALSE); 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_DEPTH_BITS, settings.getDepthBits());
glfwWindowHint(GLFW_STENCIL_BITS, settings.getStencilBits()); glfwWindowHint(GLFW_STENCIL_BITS, settings.getStencilBits());
glfwWindowHint(GLFW_SAMPLES, settings.getSamples()); glfwWindowHint(GLFW_SAMPLES, settings.getSamples());
glfwWindowHint(GLFW_STEREO, settings.useStereo3D() ? GL_TRUE : GL_FALSE); glfwWindowHint(GLFW_STEREO, settings.useStereo3D() ? GLFW_TRUE : GLFW_FALSE);
glfwWindowHint(GLFW_REFRESH_RATE, settings.getFrequency()); glfwWindowHint(GLFW_REFRESH_RATE, settings.getFrequency());
// Not sure how else to support bits per pixel
if (settings.getBitsPerPixel() == 24) { if (settings.getBitsPerPixel() == 24) {
glfwWindowHint(GLFW_RED_BITS, 8); glfwWindowHint(GLFW_RED_BITS, 8);
glfwWindowHint(GLFW_GREEN_BITS, 8); glfwWindowHint(GLFW_GREEN_BITS, 8);
@ -178,6 +178,34 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
glfwWindowHint(GLFW_ALPHA_BITS, settings.getAlphaBits()); glfwWindowHint(GLFW_ALPHA_BITS, settings.getAlphaBits());
// TODO: Add support for monitor selection
long monitor = NULL;
if (settings.isFullscreen()) {
monitor = glfwGetPrimaryMonitor();
}
final GLFWVidMode videoMode = glfwGetVideoMode(glfwGetPrimaryMonitor());
if (settings.getWidth() <= 0 || settings.getHeight() <= 0) {
settings.setResolution(videoMode.width(), videoMode.height());
}
window = glfwCreateWindow(settings.getWidth(), settings.getHeight(), settings.getTitle(), monitor, NULL);
if (window == NULL) {
throw new RuntimeException("Failed to create the GLFW window");
}
// Add a resize callback which delegates to the listener
glfwSetWindowSizeCallback(window, windowSizeCallback = new GLFWWindowSizeCallback() {
@Override
public void invoke(final long window, final int width, final int height) {
settings.setResolution(width, height);
listener.reshape(width, height);
}
});
glfwSetWindowFocusCallback(window, windowFocusCallback = new GLFWWindowFocusCallback() { glfwSetWindowFocusCallback(window, windowFocusCallback = new GLFWWindowFocusCallback() {
@Override @Override
public void invoke(final long window, final int focused) { public void invoke(final long window, final int focused) {
@ -197,8 +225,10 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
}); });
// Center the window // Center the window
if (!settings.isFullscreen() && Type.Display.equals(type)) { if (!settings.isFullscreen()) {
glfwSetWindowPos(window, (GLFWvidmode.width(videoMode) - settings.getWidth()) / 2, (GLFWvidmode.height(videoMode) - settings.getHeight()) / 2); glfwSetWindowPos(window,
(videoMode.width() - settings.getWidth()) / 2,
(videoMode.height() - settings.getHeight()) / 2);
} }
// Make the OpenGL context current // Make the OpenGL context current
@ -216,14 +246,7 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
glfwShowWindow(window); glfwShowWindow(window);
} }
// Add a resize callback which delegates to the listener glfwShowWindow(window);
glfwSetWindowSizeCallback(window, windowSizeCallback = new GLFWWindowSizeCallback() {
@Override
public void invoke(final long window, final int width, final int height) {
settings.setResolution(width, height);
listener.reshape(width, height);
}
});
allowSwapBuffers = settings.isSwapBuffers(); allowSwapBuffers = settings.isSwapBuffers();
@ -239,12 +262,24 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
renderer.cleanup(); renderer.cleanup();
} }
errorCallback.release(); if (errorCallback != null) {
windowSizeCallback.release(); errorCallback.release();
windowFocusCallback.release(); errorCallback = null;
}
if (windowSizeCallback != null) {
windowSizeCallback.release();
windowSizeCallback = null;
}
if (window != 0) { if (windowFocusCallback != null) {
windowFocusCallback.release();
windowFocusCallback = null;
}
if (window != NULL) {
glfwDestroyWindow(window); glfwDestroyWindow(window);
window = NULL;
} }
} catch (Exception ex) { } catch (Exception ex) {
listener.handleError("Failed to destroy context", ex); listener.handleError("Failed to destroy context", ex);
@ -296,8 +331,9 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
super.internalCreate(); super.internalCreate();
} catch (Exception ex) { } catch (Exception ex) {
try { try {
if (window != -1) { if (window != NULL) {
glfwDestroyWindow(window); glfwDestroyWindow(window);
window = NULL;
} }
} catch (Exception ex2) { } catch (Exception ex2) {
LOGGER.log(Level.WARNING, null, ex2); LOGGER.log(Level.WARNING, null, ex2);
@ -318,6 +354,7 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
// If a restart is required, lets recreate the context. // If a restart is required, lets recreate the context.
if (needRestart.getAndSet(false)) { if (needRestart.getAndSet(false)) {
try { try {
destroyContext();
createContext(settings); createContext(settings);
} catch (Exception ex) { } catch (Exception ex) {
LOGGER.log(Level.SEVERE, "Failed to set display settings!", ex); LOGGER.log(Level.SEVERE, "Failed to set display settings!", ex);
@ -346,8 +383,6 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
} }
} }
glfwPollEvents();
// Subclasses just call GLObjectManager clean up objects here // Subclasses just call GLObjectManager clean up objects here
// it is safe .. for now. // it is safe .. for now.
if (renderer != null) { if (renderer != null) {
@ -377,6 +412,8 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
} }
} }
} }
glfwPollEvents();
} }
private void setFrameRateLimit(int frameRateLimit) { private void setFrameRateLimit(int frameRateLimit) {
@ -389,11 +426,12 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
*/ */
protected void deinitInThread() { protected void deinitInThread() {
listener.destroy();
destroyContext(); destroyContext();
super.internalDestroy();
listener.destroy();
LOGGER.fine("Display destroyed."); LOGGER.fine("Display destroyed.");
super.internalDestroy();
} }
public void run() { public void run() {
@ -403,7 +441,7 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
} }
loadNatives(); loadNatives();
LOGGER.log(Level.FINE, "Using LWJGL {0}", Sys.getVersion()); LOGGER.log(Level.FINE, "Using LWJGL {0}", Version.getVersion());
if (!initInThread()) { if (!initInThread()) {
LOGGER.log(Level.SEVERE, "Display initialization failed. Cannot continue."); LOGGER.log(Level.SEVERE, "Display initialization failed. Cannot continue.");
@ -411,15 +449,16 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
} }
while (true) { while (true) {
if (glfwWindowShouldClose(window) == GL_TRUE) {
listener.requestClose(false);
}
runLoop(); runLoop();
if (needClose.get()) { if (needClose.get()) {
break; break;
} }
if (glfwWindowShouldClose(window) == GL_TRUE) {
listener.requestClose(false);
}
} }
deinitInThread(); deinitInThread();

Loading…
Cancel
Save