diff --git a/common.gradle b/common.gradle
index 69b695ecd..1102f49ad 100644
--- a/common.gradle
+++ b/common.gradle
@@ -16,6 +16,9 @@ repositories {
maven {
url "http://nifty-gui.sourceforge.net/nifty-maven-repo"
}
+ maven {
+ url "https://oss.sonatype.org/content/repositories/snapshots"
+ }
}
configurations {
diff --git a/jme3-core/src/main/resources/joystick-mapping.properties b/jme3-core/src/main/resources/joystick-mapping.properties
index d7ce2f50a..966832036 100644
--- a/jme3-core/src/main/resources/joystick-mapping.properties
+++ b/jme3-core/src/main/resources/joystick-mapping.properties
@@ -63,3 +63,9 @@ Xbox\ 360\ Wireless\ Receiver.AXIS_RX=z
Xbox\ 360\ Wireless\ Receiver.AXIS_RY=rz
Xbox\ 360\ Wireless\ Receiver.z=AXIS_RX
Xbox\ 360\ Wireless\ Receiver.rz=AXIS_RY
+
+# Microsoft PC-joystick driver
+Microsoft\ PC-joystick\ driver.12=POV +Y
+Microsoft\ PC-joystick\ driver.13=POV +X
+Microsoft\ PC-joystick\ driver.14=POV -Y
+Microsoft\ PC-joystick\ driver.15=POV -X
diff --git a/jme3-desktop/src/main/java/com/jme3/system/NativeLibraryLoader.java b/jme3-desktop/src/main/java/com/jme3/system/NativeLibraryLoader.java
index 2917350f6..6e83e0e6b 100644
--- a/jme3-desktop/src/main/java/com/jme3/system/NativeLibraryLoader.java
+++ b/jme3-desktop/src/main/java/com/jme3/system/NativeLibraryLoader.java
@@ -48,9 +48,8 @@ import java.util.logging.Logger;
/**
* Utility class to register, extract, and load native libraries.
*
- * Register your own libraries via the
- * {@link #registerNativeLibrary(java.lang.String, com.jme3.system.Platform, java.lang.String, boolean) }
- * method, for each platform.
+ * Register your own libraries via the {@link #registerNativeLibrary(String, Platform, String, String)} method, for
+ * each platform.
* You can then extract this library (depending on platform), by
* using {@link #loadNativeLibrary(java.lang.String, boolean) }.
*
@@ -125,23 +124,6 @@ public final class NativeLibraryLoader {
}
static {
- // LWJGL
- registerNativeLibrary("lwjgl", Platform.Windows32, "native/windows/lwjgl.dll");
- registerNativeLibrary("lwjgl", Platform.Windows64, "native/windows/lwjgl64.dll");
- registerNativeLibrary("lwjgl", Platform.Linux32, "native/linux/liblwjgl.so");
- registerNativeLibrary("lwjgl", Platform.Linux64, "native/linux/liblwjgl64.so");
- registerNativeLibrary("lwjgl", Platform.MacOSX32, "native/macosx/liblwjgl.dylib");
- registerNativeLibrary("lwjgl", Platform.MacOSX64, "native/macosx/liblwjgl.dylib");
-
- // OpenAL
- // For OSX: Need to add lib prefix when extracting
- registerNativeLibrary("openal", Platform.Windows32, "native/windows/OpenAL32.dll");
- registerNativeLibrary("openal", Platform.Windows64, "native/windows/OpenAL64.dll");
- registerNativeLibrary("openal", Platform.Linux32, "native/linux/libopenal.so");
- registerNativeLibrary("openal", Platform.Linux64, "native/linux/libopenal64.so");
- registerNativeLibrary("openal", Platform.MacOSX32, "native/macosx/openal.dylib", "libopenal.dylib");
- registerNativeLibrary("openal", Platform.MacOSX64, "native/macosx/openal.dylib", "libopenal.dylib");
-
// BulletJme
registerNativeLibrary("bulletjme", Platform.Windows32, "native/windows/x86/bulletjme.dll");
registerNativeLibrary("bulletjme", Platform.Windows64, "native/windows/x86_64/bulletjme.dll");
diff --git a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglAbstractDisplay.java b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglAbstractDisplay.java
index 7cdca0a57..415084788 100644
--- a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglAbstractDisplay.java
+++ b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglAbstractDisplay.java
@@ -40,7 +40,6 @@ import com.jme3.input.lwjgl.JInputJoyInput;
import com.jme3.input.lwjgl.LwjglKeyInput;
import com.jme3.input.lwjgl.LwjglMouseInput;
import com.jme3.system.AppSettings;
-import com.jme3.system.JmeContext.Type;
import com.jme3.system.JmeSystem;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
@@ -207,6 +206,7 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna
+ "Must set with JmeContext.setSystemListner().");
}
+ registerNatives();
loadNatives();
logger.log(Level.FINE, "Using LWJGL {0}", Sys.getVersion());
if (!initInThread()) {
diff --git a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java
index c88f7b734..70885c39b 100644
--- a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java
+++ b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java
@@ -52,12 +52,8 @@ import com.jme3.renderer.opengl.GLRenderer;
import com.jme3.renderer.opengl.GLTiming;
import com.jme3.renderer.opengl.GLTimingState;
import com.jme3.renderer.opengl.GLTracer;
-import com.jme3.system.AppSettings;
-import com.jme3.system.JmeContext;
-import com.jme3.system.JmeSystem;
-import com.jme3.system.NativeLibraryLoader;
-import com.jme3.system.SystemListener;
-import com.jme3.system.Timer;
+import com.jme3.system.*;
+
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -166,7 +162,25 @@ public abstract class LwjglContext implements JmeContext {
}
}
}
-
+
+ protected void registerNatives() {
+ // LWJGL
+ NativeLibraryLoader.registerNativeLibrary("lwjgl", Platform.Windows32, "native/windows/lwjgl.dll");
+ NativeLibraryLoader.registerNativeLibrary("lwjgl", Platform.Windows64, "native/windows/lwjgl64.dll");
+ NativeLibraryLoader.registerNativeLibrary("lwjgl", Platform.Linux32, "native/linux/liblwjgl.so");
+ NativeLibraryLoader.registerNativeLibrary("lwjgl", Platform.Linux64, "native/linux/liblwjgl64.so");
+ NativeLibraryLoader.registerNativeLibrary("lwjgl", Platform.MacOSX32, "native/macosx/liblwjgl.dylib");
+ NativeLibraryLoader.registerNativeLibrary("lwjgl", Platform.MacOSX64, "native/macosx/liblwjgl.dylib");
+
+ // For OSX: Need to add lib prefix when extracting
+ NativeLibraryLoader.registerNativeLibrary("openal", Platform.Windows32, "native/windows/OpenAL32.dll");
+ NativeLibraryLoader.registerNativeLibrary("openal", Platform.Windows64, "native/windows/OpenAL64.dll");
+ NativeLibraryLoader.registerNativeLibrary("openal", Platform.Linux32, "native/linux/libopenal.so");
+ NativeLibraryLoader.registerNativeLibrary("openal", Platform.Linux64, "native/linux/libopenal64.so");
+ NativeLibraryLoader.registerNativeLibrary("openal", Platform.MacOSX32, "native/macosx/openal.dylib", "libopenal.dylib");
+ NativeLibraryLoader.registerNativeLibrary("openal", Platform.MacOSX64, "native/macosx/openal.dylib", "libopenal.dylib");
+ }
+
protected void loadNatives() {
if (JmeSystem.isLowPermissions()) {
return;
diff --git a/jme3-lwjgl3/build.gradle b/jme3-lwjgl3/build.gradle
new file mode 100644
index 000000000..c9b43e0de
--- /dev/null
+++ b/jme3-lwjgl3/build.gradle
@@ -0,0 +1,12 @@
+if (!hasProperty('mainClass')) {
+ ext.mainClass = ''
+}
+
+dependencies {
+ compile project(':jme3-core')
+ compile project(':jme3-desktop')
+ compile 'org.lwjgl:lwjgl:3.0.0b-SNAPSHOT'
+ compile group: 'org.lwjgl', name: 'lwjgl-platform', version: '3.0.0b-SNAPSHOT', classifier: 'natives-windows'
+ compile group: 'org.lwjgl', name: 'lwjgl-platform', version: '3.0.0b-SNAPSHOT', classifier: 'natives-linux'
+ compile group: 'org.lwjgl', name: 'lwjgl-platform', version: '3.0.0b-SNAPSHOT', classifier: 'natives-osx'
+}
diff --git a/jme3-lwjgl3/src/main/java/com/jme3/audio/lwjgl/LwjglAL.java b/jme3-lwjgl3/src/main/java/com/jme3/audio/lwjgl/LwjglAL.java
new file mode 100644
index 000000000..313bdd5ee
--- /dev/null
+++ b/jme3-lwjgl3/src/main/java/com/jme3/audio/lwjgl/LwjglAL.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2009-2012 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.audio.lwjgl;
+
+import com.jme3.audio.openal.AL;
+import com.jme3.system.NativeLibraryLoader;
+import com.jme3.system.Platform;
+import org.lwjgl.openal.AL10;
+import org.lwjgl.openal.AL11;
+
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+
+public final class LwjglAL implements AL {
+
+ public LwjglAL() {
+ }
+
+ public String alGetString(int parameter) {
+ return AL10.alGetString(parameter);
+ }
+
+ public int alGenSources() {
+ return AL10.alGenSources();
+ }
+
+ public int alGetError() {
+ return AL10.alGetError();
+ }
+
+ public void alDeleteSources(int numSources, IntBuffer sources) {
+ if (sources.position() != 0) throw new AssertionError();
+ if (sources.limit() != numSources) throw new AssertionError();
+ AL10.alDeleteSources(sources);
+ }
+
+ public void alGenBuffers(int numBuffers, IntBuffer buffers) {
+ if (buffers.position() != 0) throw new AssertionError();
+ if (buffers.limit() != numBuffers) throw new AssertionError();
+ AL10.alGenBuffers(buffers);
+ }
+
+ public void alDeleteBuffers(int numBuffers, IntBuffer buffers) {
+ if (buffers.position() != 0) throw new AssertionError();
+ if (buffers.limit() != numBuffers) throw new AssertionError();
+ AL10.alDeleteBuffers(buffers);
+ }
+
+ public void alSourceStop(int source) {
+ AL10.alSourceStop(source);
+ }
+
+ public void alSourcei(int source, int param, int value) {
+ AL10.alSourcei(source, param, value);
+ }
+
+ public void alBufferData(int buffer, int format, ByteBuffer data, int size, int frequency) {
+ if (data.position() != 0) throw new AssertionError();
+ if (data.limit() != size) throw new AssertionError();
+ AL10.alBufferData(buffer, format, data, frequency);
+ }
+
+ public void alSourcePlay(int source) {
+ AL10.alSourcePlay(source);
+ }
+
+ public void alSourcePause(int source) {
+ AL10.alSourcePause(source);
+ }
+
+ public void alSourcef(int source, int param, float value) {
+ AL10.alSourcef(source, param, value);
+ }
+
+ public void alSource3f(int source, int param, float value1, float value2, float value3) {
+ AL10.alSource3f(source, param, value1, value2, value3);
+ }
+
+ public int alGetSourcei(int source, int param) {
+ return AL10.alGetSourcei(source, param);
+ }
+
+ public void alSourceUnqueueBuffers(int source, int numBuffers, IntBuffer buffers) {
+ if (buffers.position() != 0) throw new AssertionError();
+ if (buffers.limit() != numBuffers) throw new AssertionError();
+ AL10.alSourceUnqueueBuffers(source, buffers);
+ }
+
+ public void alSourceQueueBuffers(int source, int numBuffers, IntBuffer buffers) {
+ if (buffers.position() != 0) throw new AssertionError();
+ if (buffers.limit() != numBuffers) throw new AssertionError();
+ AL10.alSourceQueueBuffers(source, buffers);
+ }
+
+ public void alListener(int param, FloatBuffer data) {
+ AL10.alListenerfv(param, data);
+ }
+
+ public void alListenerf(int param, float value) {
+ AL10.alListenerf(param, value);
+ }
+
+ public void alListener3f(int param, float value1, float value2, float value3) {
+ AL10.alListener3f(param, value1, value2, value3);
+ }
+
+ public void alSource3i(int source, int param, int value1, int value2, int value3) {
+ AL11.alSource3i(source, param, value1, value2, value3);
+ }
+
+}
diff --git a/jme3-lwjgl3/src/main/java/com/jme3/audio/lwjgl/LwjglALC.java b/jme3-lwjgl3/src/main/java/com/jme3/audio/lwjgl/LwjglALC.java
new file mode 100644
index 000000000..e0aed80ee
--- /dev/null
+++ b/jme3-lwjgl3/src/main/java/com/jme3/audio/lwjgl/LwjglALC.java
@@ -0,0 +1,58 @@
+package com.jme3.audio.lwjgl;
+
+import com.jme3.audio.openal.ALC;
+import org.lwjgl.openal.ALC10;
+import org.lwjgl.openal.ALContext;
+
+import java.nio.IntBuffer;
+
+import static org.lwjgl.openal.ALC10.alcGetContextsDevice;
+import static org.lwjgl.openal.ALC10.alcGetCurrentContext;
+
+public class LwjglALC implements ALC {
+
+ private ALContext context;
+
+ public void createALC() {
+ context = ALContext.create();
+ }
+
+ public void destroyALC() {
+ if (context != null) {
+ context.destroy();
+ }
+ }
+
+ public boolean isCreated() {
+ return context != null;
+ }
+
+ public String alcGetString(final int parameter) {
+ final long context = alcGetCurrentContext();
+ final long device = alcGetContextsDevice(context);
+ return ALC10.alcGetString(device, parameter);
+ }
+
+ public boolean alcIsExtensionPresent(final String extension) {
+ final long context = alcGetCurrentContext();
+ final long device = alcGetContextsDevice(context);
+ return ALC10.alcIsExtensionPresent(device, extension);
+ }
+
+ public void alcGetInteger(final int param, final IntBuffer buffer, final int size) {
+ if (buffer.position() != 0) throw new AssertionError();
+ if (buffer.limit() != size) throw new AssertionError();
+
+ final long context = alcGetCurrentContext();
+ final long device = alcGetContextsDevice(context);
+ final int value = ALC10.alcGetInteger(device, param);
+ //buffer.put(value);
+ }
+
+ public void alcDevicePauseSOFT() {
+ }
+
+ public void alcDeviceResumeSOFT() {
+ }
+
+}
diff --git a/jme3-lwjgl3/src/main/java/com/jme3/audio/lwjgl/LwjglEFX.java b/jme3-lwjgl3/src/main/java/com/jme3/audio/lwjgl/LwjglEFX.java
new file mode 100644
index 000000000..2ddd09ed8
--- /dev/null
+++ b/jme3-lwjgl3/src/main/java/com/jme3/audio/lwjgl/LwjglEFX.java
@@ -0,0 +1,66 @@
+package com.jme3.audio.lwjgl;
+
+import com.jme3.audio.openal.EFX;
+import org.lwjgl.openal.EXTEfx;
+
+import java.nio.IntBuffer;
+
+public class LwjglEFX implements EFX {
+
+ public void alGenAuxiliaryEffectSlots(int numSlots, IntBuffer buffers) {
+ if (buffers.position() != 0) throw new AssertionError();
+ if (buffers.limit() != numSlots) throw new AssertionError();
+ EXTEfx.alGenAuxiliaryEffectSlots(buffers);
+ }
+
+ public void alGenEffects(int numEffects, IntBuffer buffers) {
+ if (buffers.position() != 0) throw new AssertionError();
+ if (buffers.limit() != numEffects) throw new AssertionError();
+ EXTEfx.alGenEffects(buffers);
+ }
+
+ public void alEffecti(int effect, int param, int value) {
+ EXTEfx.alEffecti(effect, param, value);
+ }
+
+ public void alAuxiliaryEffectSloti(int effectSlot, int param, int value) {
+ EXTEfx.alAuxiliaryEffectSloti(effectSlot, param, value);
+ }
+
+ public void alDeleteEffects(int numEffects, IntBuffer buffers) {
+ if (buffers.position() != 0) throw new AssertionError();
+ if (buffers.limit() != numEffects) throw new AssertionError();
+ EXTEfx.alDeleteEffects(buffers);
+ }
+
+ public void alDeleteAuxiliaryEffectSlots(int numEffectSlots, IntBuffer buffers) {
+ if (buffers.position() != 0) throw new AssertionError();
+ if (buffers.limit() != numEffectSlots) throw new AssertionError();
+ EXTEfx.alDeleteAuxiliaryEffectSlots(buffers);
+ }
+
+ public void alGenFilters(int numFilters, IntBuffer buffers) {
+ if (buffers.position() != 0) throw new AssertionError();
+ if (buffers.limit() != numFilters) throw new AssertionError();
+ EXTEfx.alGenFilters(buffers);
+ }
+
+ public void alFilteri(int filter, int param, int value) {
+ EXTEfx.alFilteri(filter, param, value);
+ }
+
+ public void alFilterf(int filter, int param, float value) {
+ EXTEfx.alFilterf(filter, param, value);
+ }
+
+ public void alDeleteFilters(int numFilters, IntBuffer buffers) {
+ if (buffers.position() != 0) throw new AssertionError();
+ if (buffers.limit() != numFilters) throw new AssertionError();
+ EXTEfx.alDeleteFilters(buffers);
+ }
+
+ public void alEffectf(int effect, int param, float value) {
+ EXTEfx.alEffectf(effect, param, value);
+ }
+
+}
diff --git a/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwJoystickInput.java b/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwJoystickInput.java
new file mode 100644
index 000000000..f17c08534
--- /dev/null
+++ b/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwJoystickInput.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2009-2012 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 com.jme3.input.*;
+import com.jme3.input.event.JoyAxisEvent;
+import com.jme3.input.event.JoyButtonEvent;
+import org.lwjgl.opengl.GL11;
+
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import static org.lwjgl.glfw.GLFW.*;
+
+/**
+ * @author Daniel Johansson (dannyjo)
+ * @since 3.1
+ */
+public class GlfwJoystickInput implements JoyInput {
+
+ private static final Logger LOGGER = Logger.getLogger(InputManager.class.getName());
+
+ private boolean initialized = false;
+ private RawInputListener listener;
+ private Map joysticks = new HashMap();
+
+ public void setJoyRumble(int joyId, float amount) {
+ if (joyId >= joysticks.size()) {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ @Override
+ public Joystick[] loadJoysticks(final InputManager inputManager) {
+ // TODO: Implement
+
+ for (int i = 0; i < GLFW_JOYSTICK_LAST; i++) {
+ if (glfwJoystickPresent(i) == GL11.GL_TRUE) {
+ final String name = glfwGetJoystickName(i);
+ final GlfwJoystick joystick = new GlfwJoystick(inputManager, this, i, name);
+ joysticks.put(i, joystick);
+
+ final FloatBuffer floatBuffer = glfwGetJoystickAxes(i);
+
+ int axisIndex = 0;
+ while (floatBuffer.hasRemaining()) {
+ floatBuffer.get();
+
+ final String logicalId = JoystickCompatibilityMappings.remapComponent(joystick.getName(), convertAxisIndex(axisIndex));
+ final JoystickAxis joystickAxis = new DefaultJoystickAxis(inputManager, joystick, axisIndex, convertAxisIndex(axisIndex), logicalId, true, false, 0.0f);
+ joystick.addAxis(axisIndex, joystickAxis);
+ axisIndex++;
+ }
+
+ final ByteBuffer byteBuffer = glfwGetJoystickButtons(i);
+
+ int buttonIndex = 0;
+ while (byteBuffer.hasRemaining()) {
+ byteBuffer.get();
+ final String logicalId = JoystickCompatibilityMappings.remapComponent(joystick.getName(), String.valueOf(buttonIndex));
+ joystick.addButton(new DefaultJoystickButton(inputManager, joystick, buttonIndex, String.valueOf(buttonIndex), logicalId));
+ buttonIndex++;
+ }
+ }
+ }
+
+ return joysticks.values().toArray(new GlfwJoystick[joysticks.size()]);
+ }
+
+ private String convertAxisIndex(final int index) {
+ if (index == 0) {
+ return "pov_x";
+ } else if (index == 1) {
+ return "pov_y";
+ } else if (index == 2) {
+ return "z";
+ } else if (index == 3) {
+ return "rz";
+ }
+
+ return String.valueOf(index);
+ }
+
+ public void initialize() {
+ initialized = true;
+ }
+
+ public void update() {
+ for (final Map.Entry entry : joysticks.entrySet()) {
+ // Axes
+ final FloatBuffer axisValues = glfwGetJoystickAxes(entry.getKey());
+
+ for (final JoystickAxis axis : entry.getValue().getAxes()) {
+ final float value = axisValues.get(axis.getAxisId());
+ listener.onJoyAxisEvent(new JoyAxisEvent(axis, value));
+ }
+
+ // Buttons
+ final ByteBuffer byteBuffer = glfwGetJoystickButtons(entry.getKey());
+
+ for (final JoystickButton button : entry.getValue().getButtons()) {
+ final boolean pressed = byteBuffer.get(button.getButtonId()) == GLFW_PRESS;
+ listener.onJoyButtonEvent(new JoyButtonEvent(button, pressed));
+ }
+ }
+ }
+
+ public void destroy() {
+ initialized = false;
+ }
+
+ public boolean isInitialized() {
+ return initialized;
+ }
+
+ public void setInputListener(RawInputListener listener) {
+ this.listener = listener;
+ }
+
+ public long getInputTimeNanos() {
+ return 0;
+ }
+
+ protected class GlfwJoystick extends AbstractJoystick {
+
+ private JoystickAxis povAxisX;
+ private JoystickAxis povAxisY;
+
+ public GlfwJoystick(InputManager inputManager, JoyInput joyInput, int joyId, String name) {
+ super(inputManager, joyInput, joyId, name);
+ }
+
+ public void addAxis(final int index, final JoystickAxis axis) {
+ super.addAxis(axis);
+
+ if (index == 0) {
+ povAxisX = axis;
+ } else if (index == 1) {
+ povAxisY = axis;
+ }
+ }
+
+ @Override
+ protected void addButton(JoystickButton button) {
+ super.addButton(button);
+ }
+
+ @Override
+ public JoystickAxis getXAxis() {
+ return povAxisX;
+ }
+
+ @Override
+ public JoystickAxis getYAxis() {
+ return povAxisY;
+ }
+
+ @Override
+ public JoystickAxis getPovXAxis() {
+ return povAxisX;
+ }
+
+ @Override
+ public JoystickAxis getPovYAxis() {
+ return povAxisY;
+ }
+
+ @Override
+ public int getXAxisIndex() {
+ return povAxisX.getAxisId();
+ }
+
+ @Override
+ public int getYAxisIndex() {
+ return povAxisY.getAxisId();
+ }
+ }
+}
+
+
+
diff --git a/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/LwjglKeyInput.java b/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/LwjglKeyInput.java
new file mode 100644
index 000000000..a39010c95
--- /dev/null
+++ b/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/LwjglKeyInput.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2009-2012 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 com.jme3.input.KeyInput;
+import com.jme3.input.RawInputListener;
+import com.jme3.input.event.KeyInputEvent;
+import com.jme3.system.lwjgl.LwjglTimer;
+import com.jme3.system.lwjgl.LwjglWindow;
+import org.lwjgl.glfw.GLFWKeyCallback;
+
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.logging.Logger;
+
+import static org.lwjgl.glfw.GLFW.*;
+
+public class LwjglKeyInput implements KeyInput {
+
+ private static final Logger logger = Logger.getLogger(LwjglKeyInput.class.getName());
+
+ private LwjglWindow context;
+ private RawInputListener listener;
+ private boolean initialized;
+ private GLFWKeyCallback keyCallback;
+ private Queue keyInputEvents = new LinkedList();
+
+ public LwjglKeyInput(LwjglWindow context) {
+ this.context = context;
+ }
+
+ public void initialize() {
+ if (!context.isRenderable()) {
+ return;
+ }
+
+ glfwSetKeyCallback(context.getWindowHandle(), keyCallback = new GLFWKeyCallback() {
+ @Override
+ 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);
+ evt.setTime(getInputTimeNanos());
+ keyInputEvents.add(evt);
+ }
+ });
+
+ glfwSetInputMode(context.getWindowHandle(), GLFW_STICKY_KEYS, 1);
+
+ initialized = true;
+ logger.fine("Keyboard created.");
+ }
+
+ public int getKeyCount() {
+ return 0; // TODO: How do we figure this out?
+ }
+
+ public void update() {
+ if (!context.isRenderable()) {
+ return;
+ }
+
+ while (!keyInputEvents.isEmpty()) {
+ listener.onKeyEvent(keyInputEvents.poll());
+ }
+ }
+
+ public void destroy() {
+ if (!context.isRenderable()) {
+ return;
+ }
+
+ keyCallback.release();
+ logger.fine("Keyboard destroyed.");
+ }
+
+ public boolean isInitialized() {
+ return initialized;
+ }
+
+ public void setInputListener(RawInputListener listener) {
+ this.listener = listener;
+ }
+
+ public long getInputTimeNanos() {
+ return (long) (glfwGetTime() * LwjglTimer.LWJGL_TIME_TO_NANOS);
+ }
+}
diff --git a/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/LwjglMouseInput.java b/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/LwjglMouseInput.java
new file mode 100644
index 000000000..45262ffbc
--- /dev/null
+++ b/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/LwjglMouseInput.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2009-2012 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 com.jme3.cursors.plugins.JmeCursor;
+import com.jme3.input.MouseInput;
+import com.jme3.input.RawInputListener;
+import com.jme3.input.event.MouseButtonEvent;
+import com.jme3.input.event.MouseMotionEvent;
+import com.jme3.system.lwjgl.LwjglTimer;
+import com.jme3.system.lwjgl.LwjglWindow;
+import org.lwjgl.glfw.GLFWCursorPosCallback;
+import org.lwjgl.glfw.GLFWMouseButtonCallback;
+import org.lwjgl.glfw.GLFWScrollCallback;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.logging.Logger;
+
+import static org.lwjgl.glfw.GLFW.*;
+
+public class LwjglMouseInput implements MouseInput {
+
+ private static final Logger logger = Logger.getLogger(LwjglMouseInput.class.getName());
+
+ private LwjglWindow context;
+ private RawInputListener listener;
+ private boolean cursorVisible = true;
+ private int mouseX;
+ private int mouseY;
+ private int mouseWheel;
+ private boolean initialized;
+ private GLFWCursorPosCallback cursorPosCallback;
+ private GLFWScrollCallback scrollCallback;
+ private GLFWMouseButtonCallback mouseButtonCallback;
+ private Queue mouseMotionEvents = new LinkedList();
+ private Queue mouseButtonEvents = new LinkedList();
+
+ public LwjglMouseInput(LwjglWindow context) {
+ this.context = context;
+ }
+
+ public void initialize() {
+ glfwSetCursorPosCallback(context.getWindowHandle(), cursorPosCallback = new GLFWCursorPosCallback() {
+ @Override
+ public void invoke(long window, double xpos, double ypos) {
+ int xDelta;
+ int yDelta;
+ int x = (int) Math.round(xpos);
+ int y = (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 * -1, xDelta, yDelta * -1, mouseWheel, 0);
+ mouseMotionEvent.setTime(getInputTimeNanos());
+ mouseMotionEvents.add(mouseMotionEvent);
+ }
+ }
+ });
+
+ glfwSetScrollCallback(context.getWindowHandle(), scrollCallback = new GLFWScrollCallback() {
+ @Override
+ public void invoke(final long window, final double xOffset, final double yOffset) {
+ mouseWheel += 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() {
+ @Override
+ public void invoke(final long window, final int button, final int action, final int mods) {
+ final MouseButtonEvent mouseButtonEvent = new MouseButtonEvent(button, action == GLFW_PRESS, mouseX, mouseY);
+ mouseButtonEvent.setTime(getInputTimeNanos());
+ mouseButtonEvents.add(mouseButtonEvent);
+ }
+ });
+
+ setCursorVisible(cursorVisible);
+ logger.fine("Mouse created.");
+ initialized = true;
+ }
+
+ public boolean isInitialized() {
+ return initialized;
+ }
+
+ public int getButtonCount() {
+ return 2; // TODO: How to determine this?
+ }
+
+ public void update() {
+ while (!mouseMotionEvents.isEmpty()) {
+ listener.onMouseMotionEvent(mouseMotionEvents.poll());
+ }
+
+ while (!mouseButtonEvents.isEmpty()) {
+ listener.onMouseButtonEvent(mouseButtonEvents.poll());
+ }
+ }
+
+ public void destroy() {
+ if (!context.isRenderable()) {
+ return;
+ }
+
+ cursorPosCallback.release();
+ scrollCallback.release();
+ mouseButtonCallback.release();
+
+ logger.fine("Mouse destroyed.");
+ }
+
+ public void setCursorVisible(boolean visible) {
+ cursorVisible = visible;
+
+ if (!context.isRenderable()) {
+ return;
+ }
+
+ if (cursorVisible) {
+ glfwSetInputMode(context.getWindowHandle(), GLFW_CURSOR, GLFW_CURSOR_NORMAL);
+ } else {
+ glfwSetInputMode(context.getWindowHandle(), GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+ }
+ }
+
+ public void setInputListener(RawInputListener listener) {
+ this.listener = listener;
+ }
+
+ public long getInputTimeNanos() {
+ return (long) (glfwGetTime() * LwjglTimer.LWJGL_TIME_TO_NANOS);
+ }
+
+ public void setNativeCursor(final JmeCursor jmeCursor) {
+ if (jmeCursor != null) {
+ final ByteBuffer byteBuffer = org.lwjgl.BufferUtils.createByteBuffer(jmeCursor.getImagesData().capacity());
+ byteBuffer.asIntBuffer().put(jmeCursor.getImagesData().array());
+ final long cursor = glfwCreateCursor(byteBuffer, jmeCursor.getXHotSpot(), jmeCursor.getYHotSpot());
+ glfwSetCursor(context.getWindowHandle(), cursor);
+ }
+ }
+}
diff --git a/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java
new file mode 100644
index 000000000..80e7f3513
--- /dev/null
+++ b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java
@@ -0,0 +1,458 @@
+package com.jme3.renderer.lwjgl;
+
+import com.jme3.renderer.RendererException;
+import com.jme3.renderer.opengl.GL;
+import com.jme3.renderer.opengl.GL2;
+import com.jme3.renderer.opengl.GL3;
+import com.jme3.renderer.opengl.GL4;
+import com.jme3.system.NativeLibraryLoader;
+import com.jme3.system.Platform;
+import org.lwjgl.opengl.*;
+
+import java.nio.*;
+
+public class LwjglGL implements GL, GL2, GL3, GL4 {
+
+ private static void checkLimit(Buffer buffer) {
+ if (buffer == null) {
+ return;
+ }
+ if (buffer.limit() == 0) {
+ throw new RendererException("Attempting to upload empty buffer (limit = 0), that's an error");
+ }
+ if (buffer.remaining() == 0) {
+ throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error");
+ }
+ }
+
+ public void resetStats() {
+ }
+
+ public void glActiveTexture(int param1) {
+ GL13.glActiveTexture(param1);
+ }
+
+ public void glAlphaFunc(int param1, float param2) {
+ GL11.glAlphaFunc(param1, param2);
+ }
+
+ public void glAttachShader(int param1, int param2) {
+ GL20.glAttachShader(param1, param2);
+ }
+
+ public void glBindBuffer(int param1, int param2) {
+ GL15.glBindBuffer(param1, param2);
+ }
+
+ public void glBindTexture(int param1, int param2) {
+ GL11.glBindTexture(param1, param2);
+ }
+
+ public void glBlendFunc(int param1, int param2) {
+ GL11.glBlendFunc(param1, param2);
+ }
+
+ public void glBufferData(int param1, long param2, int param3) {
+ GL15.glBufferData(param1, param2, param3);
+ }
+
+ public void glBufferData(int param1, FloatBuffer param2, int param3) {
+ checkLimit(param2);
+ GL15.glBufferData(param1, param2, param3);
+ }
+
+ public void glBufferData(int param1, ShortBuffer param2, int param3) {
+ checkLimit(param2);
+ GL15.glBufferData(param1, param2, param3);
+ }
+
+ public void glBufferData(int param1, ByteBuffer param2, int param3) {
+ checkLimit(param2);
+ GL15.glBufferData(param1, param2, param3);
+ }
+
+ public void glBufferSubData(int param1, long param2, FloatBuffer param3) {
+ checkLimit(param3);
+ GL15.glBufferSubData(param1, param2, param3);
+ }
+
+ public void glBufferSubData(int param1, long param2, ShortBuffer param3) {
+ checkLimit(param3);
+ GL15.glBufferSubData(param1, param2, param3);
+ }
+
+ public void glBufferSubData(int param1, long param2, ByteBuffer param3) {
+ checkLimit(param3);
+ GL15.glBufferSubData(param1, param2, param3);
+ }
+
+ public void glClear(int param1) {
+ GL11.glClear(param1);
+ }
+
+ public void glClearColor(float param1, float param2, float param3, float param4) {
+ GL11.glClearColor(param1, param2, param3, param4);
+ }
+
+ public void glColorMask(boolean param1, boolean param2, boolean param3, boolean param4) {
+ GL11.glColorMask(param1, param2, param3, param4);
+ }
+
+ public void glCompileShader(int param1) {
+ GL20.glCompileShader(param1);
+ }
+
+ public void glCompressedTexImage2D(int param1, int param2, int param3, int param4, int param5, int param6, ByteBuffer param7) {
+ checkLimit(param7);
+ GL13.glCompressedTexImage2D(param1, param2, param3, param4, param5, param6, param7);
+ }
+
+ public void glCompressedTexImage3D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, ByteBuffer param8) {
+ checkLimit(param8);
+ GL13.glCompressedTexImage3D(param1, param2, param3, param4, param5, param6, param7, param8);
+ }
+
+ public void glCompressedTexSubImage2D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, ByteBuffer param8) {
+ checkLimit(param8);
+ GL13.glCompressedTexSubImage2D(param1, param2, param3, param4, param5, param6, param7, param8);
+ }
+
+ public void glCompressedTexSubImage3D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, int param9, ByteBuffer param10) {
+ checkLimit(param10);
+ GL13.glCompressedTexSubImage3D(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10);
+ }
+
+ public int glCreateProgram() {
+ return GL20.glCreateProgram();
+ }
+
+ public int glCreateShader(int param1) {
+ return GL20.glCreateShader(param1);
+ }
+
+ public void glCullFace(int param1) {
+ GL11.glCullFace(param1);
+ }
+
+ public void glDeleteBuffers(IntBuffer param1) {
+ checkLimit(param1);
+ GL15.glDeleteBuffers(param1);
+ }
+
+ public void glDeleteProgram(int param1) {
+ GL20.glDeleteProgram(param1);
+ }
+
+ public void glDeleteShader(int param1) {
+ GL20.glDeleteShader(param1);
+ }
+
+ public void glDeleteTextures(IntBuffer param1) {
+ checkLimit(param1);
+ GL11.glDeleteTextures(param1);
+ }
+
+ public void glDepthFunc(int param1) {
+ GL11.glDepthFunc(param1);
+ }
+
+ public void glDepthMask(boolean param1) {
+ GL11.glDepthMask(param1);
+ }
+
+ public void glDepthRange(double param1, double param2) {
+ GL11.glDepthRange(param1, param2);
+ }
+
+ public void glDetachShader(int param1, int param2) {
+ GL20.glDetachShader(param1, param2);
+ }
+
+ public void glDisable(int param1) {
+ GL11.glDisable(param1);
+ }
+
+ public void glDisableVertexAttribArray(int param1) {
+ GL20.glDisableVertexAttribArray(param1);
+ }
+
+ public void glDrawArrays(int param1, int param2, int param3) {
+ GL11.glDrawArrays(param1, param2, param3);
+ }
+
+ public void glDrawBuffer(int param1) {
+ GL11.glDrawBuffer(param1);
+ }
+
+ public void glDrawRangeElements(int param1, int param2, int param3, int param4, int param5, long param6) {
+ GL12.glDrawRangeElements(param1, param2, param3, param4, param5, param6);
+ }
+
+ public void glEnable(int param1) {
+ GL11.glEnable(param1);
+ }
+
+ public void glEnableVertexAttribArray(int param1) {
+ GL20.glEnableVertexAttribArray(param1);
+ }
+
+ public void glGenBuffers(IntBuffer param1) {
+ checkLimit(param1);
+ GL15.glGenBuffers(param1);
+ }
+
+ public void glGenTextures(IntBuffer param1) {
+ checkLimit(param1);
+ GL11.glGenTextures(param1);
+ }
+
+ public void glGetBoolean(int param1, ByteBuffer param2) {
+ checkLimit(param2);
+ GL11.glGetBooleanv(param1, param2);
+ }
+
+ public void glGetBufferSubData(int target, long offset, ByteBuffer data) {
+ checkLimit(data);
+ GL15.glGetBufferSubData(target, offset, data);
+ }
+
+ public int glGetError() {
+ return GL11.glGetError();
+ }
+
+ public void glGetInteger(int param1, IntBuffer param2) {
+ checkLimit(param2);
+ GL11.glGetIntegerv(param1, param2);
+ }
+
+ public void glGetProgram(int param1, int param2, IntBuffer param3) {
+ checkLimit(param3);
+ GL20.glGetProgramiv(param1, param2, param3);
+ }
+
+ public void glGetShader(int param1, int param2, IntBuffer param3) {
+ checkLimit(param3);
+ GL20.glGetShaderiv(param1, param2, param3);
+ }
+
+ public String glGetString(int param1) {
+ return GL11.glGetString(param1);
+ }
+
+ public String glGetString(int param1, int param2) {
+ return GL30.glGetStringi(param1, param2);
+ }
+
+ public boolean glIsEnabled(int param1) {
+ return GL11.glIsEnabled(param1);
+ }
+
+ public void glLineWidth(float param1) {
+ GL11.glLineWidth(param1);
+ }
+
+ public void glLinkProgram(int param1) {
+ GL20.glLinkProgram(param1);
+ }
+
+ public void glPixelStorei(int param1, int param2) {
+ GL11.glPixelStorei(param1, param2);
+ }
+
+ public void glPointSize(float param1) {
+ GL11.glPointSize(param1);
+ }
+
+ public void glPolygonMode(int param1, int param2) {
+ GL11.glPolygonMode(param1, param2);
+ }
+
+ public void glPolygonOffset(float param1, float param2) {
+ GL11.glPolygonOffset(param1, param2);
+ }
+
+ public void glReadBuffer(int param1) {
+ GL11.glReadBuffer(param1);
+ }
+
+ public void glReadPixels(int param1, int param2, int param3, int param4, int param5, int param6, ByteBuffer param7) {
+ checkLimit(param7);
+ GL11.glReadPixels(param1, param2, param3, param4, param5, param6, param7);
+ }
+
+ public void glReadPixels(int param1, int param2, int param3, int param4, int param5, int param6, long param7) {
+ GL11.glReadPixels(param1, param2, param3, param4, param5, param6, param7);
+ }
+
+ public void glScissor(int param1, int param2, int param3, int param4) {
+ GL11.glScissor(param1, param2, param3, param4);
+ }
+
+ public void glStencilFuncSeparate(int param1, int param2, int param3, int param4) {
+ GL20.glStencilFuncSeparate(param1, param2, param3, param4);
+ }
+
+ public void glStencilOpSeparate(int param1, int param2, int param3, int param4) {
+ GL20.glStencilOpSeparate(param1, param2, param3, param4);
+ }
+
+ public void glTexImage2D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, ByteBuffer param9) {
+ checkLimit(param9);
+ GL11.glTexImage2D(param1, param2, param3, param4, param5, param6, param7, param8, param9);
+ }
+
+ public void glTexImage3D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, int param9, ByteBuffer param10) {
+ checkLimit(param10);
+ GL12.glTexImage3D(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10);
+ }
+
+ public void glTexParameterf(int param1, int param2, float param3) {
+ GL11.glTexParameterf(param1, param2, param3);
+ }
+
+ public void glTexParameteri(int param1, int param2, int param3) {
+ GL11.glTexParameteri(param1, param2, param3);
+ }
+
+ public void glTexSubImage2D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, ByteBuffer param9) {
+ checkLimit(param9);
+ GL11.glTexSubImage2D(param1, param2, param3, param4, param5, param6, param7, param8, param9);
+ }
+
+ public void glTexSubImage3D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, int param9, int param10, ByteBuffer param11) {
+ checkLimit(param11);
+ GL12.glTexSubImage3D(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11);
+ }
+
+ public void glUniform1(int param1, FloatBuffer param2) {
+ checkLimit(param2);
+ GL20.glUniform1fv(param1, param2);
+ }
+
+ public void glUniform1(int param1, IntBuffer param2) {
+ checkLimit(param2);
+ GL20.glUniform1iv(param1, param2);
+ }
+
+ public void glUniform1f(int param1, float param2) {
+ GL20.glUniform1f(param1, param2);
+ }
+
+ public void glUniform1i(int param1, int param2) {
+ GL20.glUniform1i(param1, param2);
+ }
+
+ public void glUniform2(int param1, IntBuffer param2) {
+ checkLimit(param2);
+ GL20.glUniform2iv(param1, param2);
+ }
+
+ public void glUniform2(int param1, FloatBuffer param2) {
+ checkLimit(param2);
+ GL20.glUniform2fv(param1, param2);
+ }
+
+ public void glUniform2f(int param1, float param2, float param3) {
+ GL20.glUniform2f(param1, param2, param3);
+ }
+
+ public void glUniform3(int param1, IntBuffer param2) {
+ checkLimit(param2);
+ GL20.glUniform3iv(param1, param2);
+ }
+
+ public void glUniform3(int param1, FloatBuffer param2) {
+ checkLimit(param2);
+ GL20.glUniform3fv(param1, param2);
+ }
+
+ public void glUniform3f(int param1, float param2, float param3, float param4) {
+ GL20.glUniform3f(param1, param2, param3, param4);
+ }
+
+ public void glUniform4(int param1, FloatBuffer param2) {
+ checkLimit(param2);
+ GL20.glUniform4fv(param1, param2);
+ }
+
+ public void glUniform4(int param1, IntBuffer param2) {
+ checkLimit(param2);
+ GL20.glUniform4iv(param1, param2);
+ }
+
+ public void glUniform4f(int param1, float param2, float param3, float param4, float param5) {
+ GL20.glUniform4f(param1, param2, param3, param4, param5);
+ }
+
+ public void glUniformMatrix3(int param1, boolean param2, FloatBuffer param3) {
+ checkLimit(param3);
+ GL20.glUniformMatrix3fv(param1, param2, param3);
+ }
+
+ public void glUniformMatrix4(int param1, boolean param2, FloatBuffer param3) {
+ checkLimit(param3);
+ GL20.glUniformMatrix4fv(param1, param2, param3);
+ }
+
+ public void glUseProgram(int param1) {
+ GL20.glUseProgram(param1);
+ }
+
+ public void glVertexAttribPointer(int param1, int param2, int param3, boolean param4, int param5, long param6) {
+ GL20.glVertexAttribPointer(param1, param2, param3, param4, param5, param6);
+ }
+
+ public void glViewport(int param1, int param2, int param3, int param4) {
+ GL11.glViewport(param1, param2, param3, param4);
+ }
+
+ public int glGetAttribLocation(int param1, String param2) {
+ // NOTE: LWJGL requires null-terminated strings
+ return GL20.glGetAttribLocation(param1, param2 + "\0");
+ }
+
+ public int glGetUniformLocation(int param1, String param2) {
+ // NOTE: LWJGL requires null-terminated strings
+ return GL20.glGetUniformLocation(param1, param2 + "\0");
+ }
+
+ public void glShaderSource(int param1, String[] param2, IntBuffer param3) {
+ checkLimit(param3);
+ GL20.glShaderSource(param1, param2);
+ }
+
+ public String glGetProgramInfoLog(int program, int maxSize) {
+ return GL20.glGetProgramInfoLog(program, maxSize);
+ }
+
+ public String glGetShaderInfoLog(int shader, int maxSize) {
+ return GL20.glGetShaderInfoLog(shader, maxSize);
+ }
+
+ @Override
+ public void glBindFragDataLocation(int param1, int param2, String param3) {
+ GL30.glBindFragDataLocation(param1, param2, param3);
+ }
+
+ @Override
+ public void glBindVertexArray(int param1) {
+ GL30.glBindVertexArray(param1);
+ }
+
+ @Override
+ public void glGenVertexArrays(IntBuffer param1) {
+ checkLimit(param1);
+ GL30.glGenVertexArrays(param1);
+ }
+
+ @Override
+ public void glPatchParameter(int count) {
+ GL40.glPatchParameteri(GL40.GL_PATCH_VERTICES,count);
+ }
+
+ @Override
+ public void glDeleteVertexArrays(IntBuffer arrays) {
+ checkLimit(arrays);
+ ARBVertexArrayObject.glDeleteVertexArrays(arrays);
+ }
+}
diff --git a/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java
new file mode 100644
index 000000000..00b3f688c
--- /dev/null
+++ b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java
@@ -0,0 +1,83 @@
+package com.jme3.renderer.lwjgl;
+
+import com.jme3.renderer.RendererException;
+import com.jme3.renderer.opengl.GLExt;
+import org.lwjgl.opengl.*;
+
+import java.nio.Buffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+
+public class LwjglGLExt implements GLExt {
+
+ private static void checkLimit(Buffer buffer) {
+ if (buffer == null) {
+ return;
+ }
+ if (buffer.limit() == 0) {
+ throw new RendererException("Attempting to upload empty buffer (limit = 0), that's an error");
+ }
+ if (buffer.remaining() == 0) {
+ throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error");
+ }
+ }
+
+ @Override
+ public void glBufferData(int target, IntBuffer data, int usage) {
+ checkLimit(data);
+ GL15.glBufferData(target, data, usage);
+ }
+
+ @Override
+ public void glBufferSubData(int target, long offset, IntBuffer data) {
+ checkLimit(data);
+ GL15.glBufferSubData(target, offset, data);
+ }
+
+ @Override
+ public void glDrawArraysInstancedARB(int mode, int first, int count, int primcount) {
+ ARBDrawInstanced.glDrawArraysInstancedARB(mode, first, count, primcount);
+ }
+
+ @Override
+ public void glDrawBuffers(IntBuffer bufs) {
+ checkLimit(bufs);
+ GL20.glDrawBuffers(bufs);
+ }
+
+ @Override
+ public void glDrawElementsInstancedARB(int mode, int indices_count, int type, long indices_buffer_offset, int primcount) {
+ ARBDrawInstanced.glDrawElementsInstancedARB(mode, indices_count, type, indices_buffer_offset, primcount);
+ }
+
+ @Override
+ public void glGetMultisample(int pname, int index, FloatBuffer val) {
+ checkLimit(val);
+ ARBTextureMultisample.glGetMultisamplefv(pname, index, val);
+ }
+
+ @Override
+ public void glTexImage2DMultisample(int target, int samples, int internalformat, int width, int height, boolean fixedsamplelocations) {
+ ARBTextureMultisample.glTexImage2DMultisample(target, samples, internalformat, width, height, fixedsamplelocations);
+ }
+
+ @Override
+ public void glVertexAttribDivisorARB(int index, int divisor) {
+ ARBInstancedArrays.glVertexAttribDivisorARB(index, divisor);
+ }
+
+ @Override
+ public Object glFenceSync(int condition, int flags) {
+ return ARBSync.glFenceSync(condition, flags);
+ }
+
+ @Override
+ public int glClientWaitSync(final Object sync, final int flags, final long timeout) {
+ return ARBSync.glClientWaitSync((Long) sync, flags, timeout);
+ }
+
+ @Override
+ public void glDeleteSync(final Object sync) {
+ ARBSync.glDeleteSync((Long) sync);
+ }
+}
diff --git a/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboEXT.java b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboEXT.java
new file mode 100644
index 000000000..969d7ae1d
--- /dev/null
+++ b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboEXT.java
@@ -0,0 +1,99 @@
+package com.jme3.renderer.lwjgl;
+
+import com.jme3.renderer.RendererException;
+import com.jme3.renderer.opengl.GLFbo;
+import org.lwjgl.opengl.EXTFramebufferBlit;
+import org.lwjgl.opengl.EXTFramebufferMultisample;
+import org.lwjgl.opengl.EXTFramebufferObject;
+
+import java.nio.Buffer;
+import java.nio.IntBuffer;
+
+/**
+ * Implements GLFbo via GL_EXT_framebuffer_object.
+ *
+ * @author Kirill Vainer
+ */
+public class LwjglGLFboEXT implements GLFbo {
+
+ private static void checkLimit(Buffer buffer) {
+ if (buffer == null) {
+ return;
+ }
+ if (buffer.limit() == 0) {
+ throw new RendererException("Attempting to upload empty buffer (limit = 0), that's an error");
+ }
+ if (buffer.remaining() == 0) {
+ throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error");
+ }
+ }
+
+ @Override
+ public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
+ EXTFramebufferBlit.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+ }
+
+ @Override
+ public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) {
+ EXTFramebufferMultisample.glRenderbufferStorageMultisampleEXT(target, samples, internalformat, width, height);
+ }
+
+ @Override
+ public void glBindFramebufferEXT(int param1, int param2) {
+ EXTFramebufferObject.glBindFramebufferEXT(param1, param2);
+ }
+
+ @Override
+ public void glBindRenderbufferEXT(int param1, int param2) {
+ EXTFramebufferObject.glBindRenderbufferEXT(param1, param2);
+ }
+
+ @Override
+ public int glCheckFramebufferStatusEXT(int param1) {
+ return EXTFramebufferObject.glCheckFramebufferStatusEXT(param1);
+ }
+
+ @Override
+ public void glDeleteFramebuffersEXT(IntBuffer param1) {
+ checkLimit(param1);
+ EXTFramebufferObject.glDeleteFramebuffersEXT(param1);
+ }
+
+ @Override
+ public void glDeleteRenderbuffersEXT(IntBuffer param1) {
+ checkLimit(param1);
+ EXTFramebufferObject.glDeleteRenderbuffersEXT(param1);
+ }
+
+ @Override
+ public void glFramebufferRenderbufferEXT(int param1, int param2, int param3, int param4) {
+ EXTFramebufferObject.glFramebufferRenderbufferEXT(param1, param2, param3, param4);
+ }
+
+ @Override
+ public void glFramebufferTexture2DEXT(int param1, int param2, int param3, int param4, int param5) {
+ EXTFramebufferObject.glFramebufferTexture2DEXT(param1, param2, param3, param4, param5);
+ }
+
+ @Override
+ public void glGenFramebuffersEXT(IntBuffer param1) {
+ checkLimit(param1);
+ EXTFramebufferObject.glGenFramebuffersEXT(param1);
+ }
+
+ @Override
+ public void glGenRenderbuffersEXT(IntBuffer param1) {
+ checkLimit(param1);
+ EXTFramebufferObject.glGenRenderbuffersEXT(param1);
+ }
+
+ @Override
+ public void glGenerateMipmapEXT(int param1) {
+ EXTFramebufferObject.glGenerateMipmapEXT(param1);
+ }
+
+ @Override
+ public void glRenderbufferStorageEXT(int param1, int param2, int param3, int param4) {
+ EXTFramebufferObject.glRenderbufferStorageEXT(param1, param2, param3, param4);
+ }
+}
diff --git a/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboGL3.java b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboGL3.java
new file mode 100644
index 000000000..aa15aeb09
--- /dev/null
+++ b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboGL3.java
@@ -0,0 +1,97 @@
+package com.jme3.renderer.lwjgl;
+
+import com.jme3.renderer.RendererException;
+import com.jme3.renderer.opengl.GLFbo;
+import org.lwjgl.opengl.GL30;
+
+import java.nio.Buffer;
+import java.nio.IntBuffer;
+
+/**
+ * Implements GLFbo via OpenGL3+.
+ *
+ * @author Kirill Vainer
+ */
+public class LwjglGLFboGL3 implements GLFbo {
+
+ private static void checkLimit(Buffer buffer) {
+ if (buffer == null) {
+ return;
+ }
+ if (buffer.limit() == 0) {
+ throw new RendererException("Attempting to upload empty buffer (limit = 0), that's an error");
+ }
+ if (buffer.remaining() == 0) {
+ throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error");
+ }
+ }
+
+ @Override
+ public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
+ GL30.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+ }
+
+ @Override
+ public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) {
+ GL30.glRenderbufferStorageMultisample(target, samples, internalformat, width, height);
+ }
+
+ @Override
+ public void glBindFramebufferEXT(int param1, int param2) {
+ GL30.glBindFramebuffer(param1, param2);
+ }
+
+ @Override
+ public void glBindRenderbufferEXT(int param1, int param2) {
+ GL30.glBindRenderbuffer(param1, param2);
+ }
+
+ @Override
+ public int glCheckFramebufferStatusEXT(int param1) {
+ return GL30.glCheckFramebufferStatus(param1);
+ }
+
+ @Override
+ public void glDeleteFramebuffersEXT(IntBuffer param1) {
+ checkLimit(param1);
+ GL30.glDeleteFramebuffers(param1);
+ }
+
+ @Override
+ public void glDeleteRenderbuffersEXT(IntBuffer param1) {
+ checkLimit(param1);
+ GL30.glDeleteRenderbuffers(param1);
+ }
+
+ @Override
+ public void glFramebufferRenderbufferEXT(int param1, int param2, int param3, int param4) {
+ GL30.glFramebufferRenderbuffer(param1, param2, param3, param4);
+ }
+
+ @Override
+ public void glFramebufferTexture2DEXT(int param1, int param2, int param3, int param4, int param5) {
+ GL30.glFramebufferTexture2D(param1, param2, param3, param4, param5);
+ }
+
+ @Override
+ public void glGenFramebuffersEXT(IntBuffer param1) {
+ checkLimit(param1);
+ GL30.glGenFramebuffers(param1);
+ }
+
+ @Override
+ public void glGenRenderbuffersEXT(IntBuffer param1) {
+ checkLimit(param1);
+ GL30.glGenRenderbuffers(param1);
+ }
+
+ @Override
+ public void glGenerateMipmapEXT(int param1) {
+ GL30.glGenerateMipmap(param1);
+ }
+
+ @Override
+ public void glRenderbufferStorageEXT(int param1, int param2, int param3, int param4) {
+ GL30.glRenderbufferStorage(param1, param2, param3, param4);
+ }
+}
diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglCanvas.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglCanvas.java
new file mode 100644
index 000000000..1f1ef2ab5
--- /dev/null
+++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglCanvas.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2009-2012 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.system.lwjgl;
+
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeCanvasContext;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static org.lwjgl.glfw.GLFW.glfwDestroyWindow;
+
+public class LwjglCanvas extends LwjglWindow implements JmeCanvasContext {
+
+ protected static final int TASK_NOTHING = 0,
+ TASK_DESTROY_DISPLAY = 1,
+ TASK_CREATE_DISPLAY = 2,
+ TASK_COMPLETE = 3;
+
+// protected static final boolean USE_SHARED_CONTEXT =
+// Boolean.parseBoolean(System.getProperty("jme3.canvas.sharedctx", "true"));
+
+ protected static final boolean USE_SHARED_CONTEXT = false;
+
+ private static final Logger logger = Logger.getLogger(LwjglCanvas.class.getName());
+ private Canvas canvas;
+ private int width;
+ private int height;
+
+ private final Object taskLock = new Object();
+ private int desiredTask = TASK_NOTHING;
+
+ private Thread renderThread;
+ private boolean runningFirstTime = true;
+ private boolean mouseWasGrabbed = false;
+
+ private boolean mouseWasCreated = false;
+ private boolean keyboardWasCreated = false;
+
+ private long window;
+
+ private class GLCanvas extends Canvas {
+ @Override
+ public void addNotify(){
+ super.addNotify();
+
+ if (renderThread != null && renderThread.getState() == Thread.State.TERMINATED) {
+ return; // already destroyed.
+ }
+
+ if (renderThread == null){
+ logger.log(Level.FINE, "EDT: Creating OGL thread.");
+
+ // Also set some settings on the canvas here.
+ // So we don't do it outside the AWT thread.
+ canvas.setFocusable(true);
+ canvas.setIgnoreRepaint(true);
+
+ renderThread = new Thread(LwjglCanvas.this, THREAD_NAME);
+ renderThread.start();
+ }else if (needClose.get()){
+ return;
+ }
+
+ logger.log(Level.FINE, "EDT: Telling OGL to create display ..");
+ synchronized (taskLock){
+ desiredTask = TASK_CREATE_DISPLAY;
+// while (desiredTask != TASK_COMPLETE){
+// try {
+// taskLock.wait();
+// } catch (InterruptedException ex) {
+// return;
+// }
+// }
+// desiredTask = TASK_NOTHING;
+ }
+// logger.log(Level.FINE, "EDT: OGL has created the display");
+ }
+
+ @Override
+ public void removeNotify(){
+ if (needClose.get()){
+ logger.log(Level.FINE, "EDT: Application is stopped. Not restoring canvas.");
+ super.removeNotify();
+ return;
+ }
+
+ // We must tell GL context to shutdown and wait for it to
+ // shutdown, otherwise, issues will occur.
+ logger.log(Level.FINE, "EDT: Telling OGL to destroy display ..");
+ synchronized (taskLock){
+ desiredTask = TASK_DESTROY_DISPLAY;
+ while (desiredTask != TASK_COMPLETE){
+ try {
+ taskLock.wait();
+ } catch (InterruptedException ex){
+ super.removeNotify();
+ return;
+ }
+ }
+ desiredTask = TASK_NOTHING;
+ }
+
+ logger.log(Level.FINE, "EDT: Acknowledged receipt of canvas death");
+ // GL context is dead at this point
+
+ super.removeNotify();
+ }
+ }
+
+ public LwjglCanvas(){
+ super(Type.Canvas);
+ canvas = new GLCanvas();
+ }
+
+ public void create(boolean waitFor){
+ if (renderThread == null){
+ logger.log(Level.FINE, "MAIN: Creating OGL thread.");
+
+ renderThread = new Thread(LwjglCanvas.this, THREAD_NAME);
+ renderThread.start();
+ }
+ // do not do anything.
+ // superclass's create() will be called at initInThread()
+ if (waitFor) {
+ waitFor(true);
+ }
+ }
+
+ public Canvas getCanvas(){
+ return canvas;
+ }
+
+ @Override
+ protected void runLoop(){
+ if (desiredTask != TASK_NOTHING){
+ synchronized (taskLock){
+ switch (desiredTask){
+ case TASK_CREATE_DISPLAY:
+ logger.log(Level.FINE, "OGL: Creating display ..");
+ restoreCanvas();
+ listener.gainFocus();
+ desiredTask = TASK_NOTHING;
+ break;
+ case TASK_DESTROY_DISPLAY:
+ logger.log(Level.FINE, "OGL: Destroying display ..");
+ listener.loseFocus();
+ pauseCanvas();
+ break;
+ }
+ desiredTask = TASK_COMPLETE;
+ taskLock.notifyAll();
+ }
+ }
+
+ if (renderable.get()){
+ int newWidth = Math.max(canvas.getWidth(), 1);
+ int newHeight = Math.max(canvas.getHeight(), 1);
+
+ if (width != newWidth || height != newHeight){
+ width = newWidth;
+ height = newHeight;
+ if (listener != null){
+ listener.reshape(width, height);
+ }
+ }
+ }
+
+ super.runLoop();
+ }
+
+ private void pauseCanvas(){
+ if (mouseInput != null) {
+ mouseInput.setCursorVisible(true);
+ mouseWasCreated = true;
+ }
+
+/*
+ if (Mouse.isCreated()){
+ if (Mouse.isGrabbed()){
+ Mouse.setGrabbed(false);
+ mouseWasGrabbed = true;
+ }
+ mouseWasCreated = true;
+ Mouse.destroy();
+ }
+ if (Keyboard.isCreated()){
+ keyboardWasCreated = true;
+ Keyboard.destroy();
+ }
+*/
+
+ renderable.set(false);
+ destroyContext();
+ }
+
+ /**
+ * Called to restore the canvas.
+ */
+ private void restoreCanvas(){
+ logger.log(Level.FINE, "OGL: Waiting for canvas to become displayable..");
+ while (!canvas.isDisplayable()){
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException ex) {
+ logger.log(Level.SEVERE, "OGL: Interrupted! ", ex);
+ }
+ }
+
+ logger.log(Level.FINE, "OGL: Creating display context ..");
+
+ // Set renderable to true, since canvas is now displayable.
+ renderable.set(true);
+ createContext(settings);
+
+ logger.log(Level.FINE, "OGL: Display is active!");
+
+ try {
+ if (mouseWasCreated){
+// Mouse.create();
+// if (mouseWasGrabbed){
+// Mouse.setGrabbed(true);
+// mouseWasGrabbed = false;
+// }
+ }
+ if (keyboardWasCreated){
+// Keyboard.create();
+// keyboardWasCreated = false;
+ }
+ } catch (Exception ex){
+ logger.log(Level.SEVERE, "Encountered exception when restoring input", ex);
+ }
+
+ SwingUtilities.invokeLater(new Runnable(){
+ public void run(){
+ canvas.requestFocus();
+ }
+ });
+ }
+
+/*
+ */
+/**
+ * Makes sure the pbuffer is available and ready for use
+ *//*
+
+ protected void makePbufferAvailable() throws LWJGLException{
+ if (pbuffer != null && pbuffer.isBufferLost()){
+ logger.log(Level.WARNING, "PBuffer was lost!");
+ pbuffer.destroy();
+ pbuffer = null;
+ }
+
+ if (pbuffer == null) {
+ pbuffer = new Pbuffer(1, 1, acquirePixelFormat(true), null);
+ pbuffer.makeCurrent();
+ logger.log(Level.FINE, "OGL: Pbuffer has been created");
+
+ // Any created objects are no longer valid
+ if (!runningFirstTime){
+ renderer.resetGLObjects();
+ }
+ }
+
+ pbuffer.makeCurrent();
+ if (!pbuffer.isCurrent()){
+ throw new LWJGLException("Pbuffer cannot be made current");
+ }
+ }
+
+ protected void destroyPbuffer(){
+ if (pbuffer != null){
+ if (!pbuffer.isBufferLost()){
+ pbuffer.destroy();
+ }
+ pbuffer = null;
+ }
+ }
+*/
+
+ /**
+ * This is called:
+ * 1) When the context thread ends
+ * 2) Any time the canvas becomes non-displayable
+ */
+ protected void destroyContext(){
+ // invalidate the state so renderer can resume operation
+ if (!USE_SHARED_CONTEXT){
+ renderer.cleanup();
+ }
+
+ if (window != 0) {
+ glfwDestroyWindow(window);
+ }
+
+ // TODO: Destroy input
+
+
+ // The canvas is no longer visible,
+ // but the context thread is still running.
+ if (!needClose.get()){
+ renderer.invalidateState();
+ }
+ }
+
+ /**
+ * This is called:
+ * 1) When the context thread starts
+ * 2) Any time the canvas becomes displayable again.
+ */
+ @Override
+ protected void createContext(final AppSettings settings) {
+ // In case canvas is not visible, we still take framerate
+ // from settings to prevent "100% CPU usage"
+ allowSwapBuffers = settings.isSwapBuffers();
+
+ if (renderable.get()){
+ if (!runningFirstTime){
+ // because the display is a different opengl context
+ // must reset the context state.
+ if (!USE_SHARED_CONTEXT){
+ renderer.cleanup();
+ }
+ }
+
+ super.createContext(settings);
+ }
+
+ // At this point, the OpenGL context is active.
+ if (runningFirstTime) {
+ // THIS is the part that creates the renderer.
+ // It must always be called, now that we have the pbuffer workaround.
+ initContextFirstTime();
+ runningFirstTime = false;
+ }
+ }
+}
diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java
new file mode 100644
index 000000000..3f6491e5f
--- /dev/null
+++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2009-2012 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.system.lwjgl;
+
+import com.jme3.input.lwjgl.GlfwJoystickInput;
+import com.jme3.input.lwjgl.LwjglKeyInput;
+import com.jme3.input.lwjgl.LwjglMouseInput;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.RendererException;
+import com.jme3.renderer.lwjgl.LwjglGL;
+import com.jme3.renderer.lwjgl.LwjglGLExt;
+import com.jme3.renderer.lwjgl.LwjglGLFboEXT;
+import com.jme3.renderer.lwjgl.LwjglGLFboGL3;
+import com.jme3.renderer.opengl.*;
+import com.jme3.renderer.opengl.GL;
+import com.jme3.system.*;
+import org.lwjgl.Sys;
+import org.lwjgl.glfw.GLFW;
+import org.lwjgl.opengl.*;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static org.lwjgl.opengl.GL.createCapabilities;
+import static org.lwjgl.opengl.GL11.GL_TRUE;
+import static org.lwjgl.opengl.GL11.glGetInteger;
+
+/**
+ * A LWJGL implementation of a graphics context.
+ */
+public abstract class LwjglContext implements JmeContext {
+
+ private static final Logger logger = Logger.getLogger(LwjglContext.class.getName());
+
+ protected static final String THREAD_NAME = "jME3 Main";
+
+ protected AtomicBoolean created = new AtomicBoolean(false);
+ protected AtomicBoolean renderable = new AtomicBoolean(false);
+ protected final Object createdLock = new Object();
+
+ protected AppSettings settings = new AppSettings(true);
+ protected Renderer renderer;
+ protected LwjglKeyInput keyInput;
+ protected LwjglMouseInput mouseInput;
+ protected GlfwJoystickInput joyInput;
+ protected Timer timer;
+ protected SystemListener listener;
+
+ public void setSystemListener(SystemListener listener) {
+ this.listener = listener;
+ }
+
+ protected void printContextInitInfo() {
+ logger.log(Level.INFO, "LWJGL {0} context running on thread {1}\n" +
+ " * Graphics Adapter: GLFW {2}",
+ new Object[]{Sys.getVersion(), Thread.currentThread().getName(), GLFW.glfwGetVersionString()});
+ }
+
+ protected int determineMaxSamples() {
+ // If we already have a valid context, determine samples using current context.
+ if (GLFW.glfwExtensionSupported("GL_ARB_framebuffer_object") == GL_TRUE) {
+ return glGetInteger(ARBFramebufferObject.GL_MAX_SAMPLES);
+ } else if (GLFW.glfwExtensionSupported("GL_EXT_framebuffer_multisample") == GL_TRUE) {
+ return glGetInteger(EXTFramebufferMultisample.GL_MAX_SAMPLES_EXT);
+ }
+
+ return Integer.MAX_VALUE;
+ }
+
+ protected void registerNatives() {
+ // LWJGL
+ NativeLibraryLoader.registerNativeLibrary("lwjgl", Platform.Windows32, "native/windows/lwjgl32.dll");
+ NativeLibraryLoader.registerNativeLibrary("lwjgl", Platform.Windows64, "native/windows/lwjgl.dll");
+ NativeLibraryLoader.registerNativeLibrary("lwjgl", Platform.Linux32, "native/linux/liblwjgl32.so");
+ NativeLibraryLoader.registerNativeLibrary("lwjgl", Platform.Linux64, "native/linux/liblwjgl.so");
+ NativeLibraryLoader.registerNativeLibrary("lwjgl", Platform.MacOSX32, "native/macosx/liblwjgl.dylib");
+ NativeLibraryLoader.registerNativeLibrary("lwjgl", Platform.MacOSX64, "native/macosx/liblwjgl.dylib");
+ NativeLibraryLoader.registerNativeLibrary("lwjgl", Platform.Windows32, "native/windows/jemalloc32.dll");
+ NativeLibraryLoader.registerNativeLibrary("lwjgl", Platform.Windows64, "native/windows/jemalloc.dll");
+
+ // OpenAL
+ // For OSX: Need to add lib prefix when extracting
+ NativeLibraryLoader.registerNativeLibrary("openal", Platform.Windows32, "native/windows/OpenAL32.dll");
+ NativeLibraryLoader.registerNativeLibrary("openal", Platform.Windows64, "native/windows/OpenAL.dll");
+ NativeLibraryLoader.registerNativeLibrary("openal", Platform.Linux32, "native/linux/libopenal32.so");
+ NativeLibraryLoader.registerNativeLibrary("openal", Platform.Linux64, "native/linux/libopenal.so");
+ NativeLibraryLoader.registerNativeLibrary("openal", Platform.MacOSX32, "native/macosx/openal.dylib", "libopenal.dylib");
+ NativeLibraryLoader.registerNativeLibrary("openal", Platform.MacOSX64, "native/macosx/openal.dylib", "libopenal.dylib");
+ }
+
+ protected void loadNatives() {
+ if (JmeSystem.isLowPermissions()) {
+ return;
+ }
+ if ("LWJGL".equals(settings.getAudioRenderer())) {
+ NativeLibraryLoader.loadNativeLibrary("openal", true);
+ }
+ if (settings.useJoysticks()) {
+ //NativeLibraryLoader.loadNativeLibrary("jinput", true);
+ //NativeLibraryLoader.loadNativeLibrary("jinput-dx8", true);
+ }
+ if (NativeLibraryLoader.isUsingNativeBullet()) {
+ NativeLibraryLoader.loadNativeLibrary("bulletjme", true);
+ }
+ NativeLibraryLoader.loadNativeLibrary("lwjgl", true);
+ }
+
+ protected int getNumSamplesToUse() {
+ int samples = 0;
+ if (settings.getSamples() > 1) {
+ samples = settings.getSamples();
+ final int supportedSamples = determineMaxSamples();
+ if (supportedSamples < samples) {
+ logger.log(Level.WARNING,
+ "Couldn't satisfy antialiasing samples requirement: x{0}. "
+ + "Video hardware only supports: x{1}",
+ new Object[]{samples, supportedSamples});
+
+ samples = supportedSamples;
+ }
+ }
+ return samples;
+ }
+
+ protected void initContextFirstTime() {
+ final GLCapabilities capabilities = createCapabilities(settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3));
+
+ if (!capabilities.OpenGL20) {
+ throw new RendererException("OpenGL 2.0 or higher is required for jMonkeyEngine");
+ }
+
+ if (settings.getRenderer().equals(AppSettings.LWJGL_OPENGL2)
+ || settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)) {
+ GL gl = new LwjglGL();
+ GLExt glext = new LwjglGLExt();
+ GLFbo glfbo;
+
+ if (capabilities.OpenGL30) {
+ glfbo = new LwjglGLFboGL3();
+ } else {
+ glfbo = new LwjglGLFboEXT();
+ }
+
+ if (settings.getBoolean("GraphicsDebug")) {
+ gl = new GLDebugDesktop(gl, glext, glfbo);
+ glext = (GLExt) gl;
+ glfbo = (GLFbo) gl;
+ }
+
+ if (settings.getBoolean("GraphicsTiming")) {
+ GLTimingState timingState = new GLTimingState();
+ gl = (GL) GLTiming.createGLTiming(gl, timingState, GL.class, GL2.class, GL3.class, GL4.class);
+ glext = (GLExt) GLTiming.createGLTiming(glext, timingState, GLExt.class);
+ glfbo = (GLFbo) GLTiming.createGLTiming(glfbo, timingState, GLFbo.class);
+ }
+
+ if (settings.getBoolean("GraphicsTrace")) {
+ gl = (GL) GLTracer.createDesktopGlTracer(gl, GL.class, GL2.class, GL3.class, GL4.class);
+ glext = (GLExt) GLTracer.createDesktopGlTracer(glext, GLExt.class);
+ glfbo = (GLFbo) GLTracer.createDesktopGlTracer(glfbo, GLFbo.class);
+ }
+
+ renderer = new GLRenderer(gl, glext, glfbo);
+ renderer.initialize();
+ } else {
+ throw new UnsupportedOperationException("Unsupported renderer: " + settings.getRenderer());
+ }
+
+ 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.
+ }
+
+ renderer.setMainFrameBufferSrgb(settings.getGammaCorrection());
+ renderer.setLinearizeSrgbImages(settings.getGammaCorrection());
+
+ // Init input
+ if (keyInput != null) {
+ keyInput.initialize();
+ }
+
+ if (mouseInput != null) {
+ mouseInput.initialize();
+ }
+
+ if (joyInput != null) {
+ joyInput.initialize();
+ }
+
+ renderable.set(true);
+ }
+
+ public void internalDestroy() {
+ renderer = null;
+ timer = null;
+ renderable.set(false);
+ synchronized (createdLock) {
+ created.set(false);
+ createdLock.notifyAll();
+ }
+ }
+
+ public void internalCreate() {
+ synchronized (createdLock) {
+ created.set(true);
+ createdLock.notifyAll();
+ }
+
+ //if (renderable.get()) {
+ initContextFirstTime();
+ //} else {
+// assert getType() == Type.Canvas;
+// }
+ }
+
+ public void create() {
+ create(false);
+ }
+
+ public void destroy() {
+ destroy(false);
+ }
+
+ protected void waitFor(boolean createdVal) {
+ synchronized (createdLock) {
+ while (created.get() != createdVal) {
+ try {
+ createdLock.wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ }
+ }
+
+ public boolean isCreated() {
+ return created.get();
+ }
+
+ public boolean isRenderable() {
+ return renderable.get();
+ }
+
+ public void setSettings(AppSettings settings) {
+ this.settings.copyFrom(settings);
+ }
+
+ public AppSettings getSettings() {
+ return settings;
+ }
+
+ public Renderer getRenderer() {
+ return renderer;
+ }
+
+ public Timer getTimer() {
+ return timer;
+ }
+
+}
diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglDisplay.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglDisplay.java
new file mode 100644
index 000000000..4a13c30af
--- /dev/null
+++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglDisplay.java
@@ -0,0 +1,12 @@
+package com.jme3.system.lwjgl;
+
+/**
+ * @author Daniel Johansson
+ * @since 2015-08-11
+ */
+public class LwjglDisplay extends LwjglWindow {
+
+ public LwjglDisplay() {
+ super(Type.Display);
+ }
+}
diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglGLDebugOutputHandler.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglGLDebugOutputHandler.java
new file mode 100644
index 000000000..aa9c46511
--- /dev/null
+++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglGLDebugOutputHandler.java
@@ -0,0 +1,78 @@
+/*
+ * 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.system.lwjgl;
+
+import org.lwjgl.opengl.ARBDebugOutput;
+import org.lwjgl.opengl.GLDebugMessageARBCallback;
+
+import java.util.HashMap;
+
+class LwjglGLDebugOutputHandler extends GLDebugMessageARBCallback {
+
+ private static final HashMap constMap = new HashMap();
+ private static final String MESSAGE_FORMAT =
+ "[JME3] OpenGL debug message\r\n" +
+ " ID: %d\r\n" +
+ " Source: %s\r\n" +
+ " Type: %s\r\n" +
+ " Severity: %s\r\n" +
+ " Message: %s";
+
+ static {
+ constMap.put(ARBDebugOutput.GL_DEBUG_SOURCE_API_ARB, "API");
+ constMap.put(ARBDebugOutput.GL_DEBUG_SOURCE_APPLICATION_ARB, "APPLICATION");
+ constMap.put(ARBDebugOutput.GL_DEBUG_SOURCE_OTHER_ARB, "OTHER");
+ constMap.put(ARBDebugOutput.GL_DEBUG_SOURCE_SHADER_COMPILER_ARB, "SHADER_COMPILER");
+ constMap.put(ARBDebugOutput.GL_DEBUG_SOURCE_THIRD_PARTY_ARB, "THIRD_PARTY");
+ constMap.put(ARBDebugOutput.GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB, "WINDOW_SYSTEM");
+
+ constMap.put(ARBDebugOutput.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB, "DEPRECATED_BEHAVIOR");
+ constMap.put(ARBDebugOutput.GL_DEBUG_TYPE_ERROR_ARB, "ERROR");
+ constMap.put(ARBDebugOutput.GL_DEBUG_TYPE_OTHER_ARB, "OTHER");
+ constMap.put(ARBDebugOutput.GL_DEBUG_TYPE_PERFORMANCE_ARB, "PERFORMANCE");
+ constMap.put(ARBDebugOutput.GL_DEBUG_TYPE_PORTABILITY_ARB, "PORTABILITY");
+ constMap.put(ARBDebugOutput.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB, "UNDEFINED_BEHAVIOR");
+
+ constMap.put(ARBDebugOutput.GL_DEBUG_SEVERITY_HIGH_ARB, "HIGH");
+ constMap.put(ARBDebugOutput.GL_DEBUG_SEVERITY_MEDIUM_ARB, "MEDIUM");
+ constMap.put(ARBDebugOutput.GL_DEBUG_SEVERITY_LOW_ARB, "LOW");
+ }
+
+ @Override
+ public void invoke(int source, int type, int id, int severity, int length, long message, long userParam) {
+ String sourceStr = constMap.get(source);
+ String typeStr = constMap.get(type);
+ String severityStr = constMap.get(severity);
+
+ System.err.println(String.format(MESSAGE_FORMAT, id, sourceStr, typeStr, severityStr, message));
+ }
+}
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
new file mode 100644
index 000000000..e374e1841
--- /dev/null
+++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java
@@ -0,0 +1,14 @@
+package com.jme3.system.lwjgl;
+
+import com.jme3.system.JmeContext;
+
+/**
+ * @author Daniel Johansson
+ * @since 2015-08-11
+ */
+public class LwjglOffscreenBuffer extends LwjglWindow {
+
+ public LwjglOffscreenBuffer() {
+ super(JmeContext.Type.OffscreenSurface);
+ }
+}
diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglSmoothingTimer.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglSmoothingTimer.java
new file mode 100644
index 000000000..a7960a261
--- /dev/null
+++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglSmoothingTimer.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2009-2012 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.system.lwjgl;
+
+import com.jme3.math.FastMath;
+import com.jme3.system.Timer;
+import org.lwjgl.glfw.GLFW;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Timer
handles the system's time related functionality. This
+ * allows the calculation of the framerate. To keep the framerate calculation
+ * accurate, a call to update each frame is required. Timer
is a
+ * singleton object and must be created via the getTimer
method.
+ *
+ * @author Mark Powell
+ * @version $Id: LWJGLTimer.java,v 1.21 2007/09/22 16:46:35 irrisor Exp $
+ */
+public class LwjglSmoothingTimer extends LwjglTimer {
+ private static final Logger logger = Logger.getLogger(LwjglSmoothingTimer.class
+ .getName());
+
+ private long lastFrameDiff;
+
+ //frame rate parameters.
+ private long oldTime;
+
+ private float lastTPF, lastFPS;
+
+ public static int TIMER_SMOOTHNESS = 32;
+
+ private long[] tpf;
+
+ private int smoothIndex;
+
+ private final static long LWJGL_TIMER_RES = 1;
+ private final static float INV_LWJGL_TIMER_RES = ( 1f / LWJGL_TIMER_RES );
+ private static float invTimerRezSmooth;
+
+ public final static long LWJGL_TIME_TO_NANOS = (1000000000 / LWJGL_TIMER_RES);
+
+ private long startTime;
+
+ private boolean allSmooth = false;
+
+ /**
+ * Constructor builds a Timer
object. All values will be
+ * initialized to it's default values.
+ */
+ public LwjglSmoothingTimer() {
+ reset();
+
+ //print timer resolution info
+ logger.log(Level.FINE, "Timer resolution: {0} ticks per second", LWJGL_TIMER_RES);
+ }
+
+ public void reset() {
+ lastFrameDiff = 0;
+ lastFPS = 0;
+ lastTPF = 0;
+
+ // init to -1 to indicate this is a new timer.
+ oldTime = -1;
+ //reset time
+ startTime = (long) (GLFW.glfwGetTime() * LWJGL_TIME_TO_NANOS);
+
+ tpf = new long[TIMER_SMOOTHNESS];
+ smoothIndex = TIMER_SMOOTHNESS - 1;
+ invTimerRezSmooth = ( 1f / (LWJGL_TIMER_RES * TIMER_SMOOTHNESS));
+
+ // set tpf... -1 values will not be used for calculating the average in update()
+ for ( int i = tpf.length; --i >= 0; ) {
+ tpf[i] = -1;
+ }
+ }
+
+ /**
+ * @see Timer#getResolution()
+ */
+ public long getResolution() {
+ return LWJGL_TIMER_RES;
+ }
+
+ /**
+ * getFrameRate
returns the current frame rate since the last
+ * call to update
.
+ *
+ * @return the current frame rate.
+ */
+ public float getFrameRate() {
+ return lastFPS;
+ }
+
+ public float getTimePerFrame() {
+ return lastTPF;
+ }
+
+ /**
+ * update
recalulates the frame rate based on the previous
+ * call to update. It is assumed that update is called each frame.
+ */
+ public void update() {
+ long newTime = (long) (GLFW.glfwGetTime() * LWJGL_TIME_TO_NANOS);
+ long oldTime = this.oldTime;
+ this.oldTime = newTime;
+ if ( oldTime == -1 ) {
+ // For the first frame use 60 fps. This value will not be counted in further averages.
+ // This is done so initialization code between creating the timer and the first
+ // frame is not counted as a single frame on it's own.
+ lastTPF = 1 / 60f;
+ lastFPS = 1f / lastTPF;
+ return;
+ }
+
+ long frameDiff = newTime - oldTime;
+ long lastFrameDiff = this.lastFrameDiff;
+ if ( lastFrameDiff > 0 && frameDiff > lastFrameDiff *100 ) {
+ frameDiff = lastFrameDiff *100;
+ }
+ this.lastFrameDiff = frameDiff;
+ tpf[smoothIndex] = frameDiff;
+ smoothIndex--;
+ if ( smoothIndex < 0 ) {
+ smoothIndex = tpf.length - 1;
+ }
+
+ lastTPF = 0.0f;
+ if (!allSmooth) {
+ int smoothCount = 0;
+ for ( int i = tpf.length; --i >= 0; ) {
+ if ( tpf[i] != -1 ) {
+ lastTPF += tpf[i];
+ smoothCount++;
+ }
+ }
+ if (smoothCount == tpf.length)
+ allSmooth = true;
+ lastTPF *= ( INV_LWJGL_TIMER_RES / smoothCount );
+ } else {
+ for ( int i = tpf.length; --i >= 0; ) {
+ if ( tpf[i] != -1 ) {
+ lastTPF += tpf[i];
+ }
+ }
+ lastTPF *= invTimerRezSmooth;
+ }
+ if ( lastTPF < FastMath.FLT_EPSILON ) {
+ lastTPF = FastMath.FLT_EPSILON;
+ }
+
+ lastFPS = 1f / lastTPF;
+ }
+
+ /**
+ * toString
returns the string representation of this timer
+ * in the format:
+ *
+ * jme.utility.Timer@1db699b
+ * Time: {LONG}
+ * FPS: {LONG}
+ *
+ * @return the string representation of this object.
+ */
+ @Override
+ public String toString() {
+ String string = super.toString();
+ string += "\nTime: " + oldTime;
+ string += "\nFPS: " + getFrameRate();
+ return string;
+ }
+}
\ No newline at end of file
diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglTimer.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglTimer.java
new file mode 100644
index 000000000..c4a0e5025
--- /dev/null
+++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglTimer.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2009-2012 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.system.lwjgl;
+
+import com.jme3.system.Timer;
+import org.lwjgl.glfw.GLFW;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Timer
handles the system's time related functionality. This
+ * allows the calculation of the framerate. To keep the framerate calculation
+ * accurate, a call to update each frame is required. Timer
is a
+ * singleton object and must be created via the getTimer
method.
+ *
+ * @author Mark Powell
+ * @version $Id: LWJGLTimer.java,v 1.21 2007/09/22 16:46:35 irrisor Exp $
+ */
+public class LwjglTimer extends Timer {
+
+ private static final Logger logger = Logger.getLogger(LwjglTimer.class.getName());
+
+ //frame rate parameters.
+ private long oldTime;
+ private long startTime;
+
+ private float lastTPF, lastFPS;
+
+ private final static long LWJGL_TIMER_RES = 1;
+ private final static float INV_LWJGL_TIMER_RES = ( 1f / LWJGL_TIMER_RES );
+ public final static long LWJGL_TIME_TO_NANOS = (1000000000 / LWJGL_TIMER_RES);
+
+ /**
+ * Constructor builds a Timer
object. All values will be
+ * initialized to it's default values.
+ */
+ public LwjglTimer() {
+ reset();
+ logger.log(Level.FINE, "Timer resolution: {0} ticks per second", LWJGL_TIMER_RES);
+ }
+
+ public void reset() {
+ startTime = (long) (GLFW.glfwGetTime() * LWJGL_TIME_TO_NANOS);
+ oldTime = getTime();
+ }
+
+ @Override
+ public float getTimeInSeconds() {
+ return getTime() * INV_LWJGL_TIMER_RES;
+ }
+
+ /**
+ * @see Timer#getTime()
+ */
+ public long getTime() {
+ return ((long) (GLFW.glfwGetTime() * LWJGL_TIME_TO_NANOS) - startTime);
+ }
+
+ /**
+ * @see Timer#getResolution()
+ */
+ public long getResolution() {
+ return LWJGL_TIMER_RES;
+ }
+
+ /**
+ * getFrameRate
returns the current frame rate since the last
+ * call to update
.
+ *
+ * @return the current frame rate.
+ */
+ public float getFrameRate() {
+ return lastFPS;
+ }
+
+ public float getTimePerFrame() {
+ return lastTPF;
+ }
+
+ /**
+ * update
recalulates the frame rate based on the previous
+ * call to update. It is assumed that update is called each frame.
+ */
+ public void update() {
+ long curTime = getTime();
+ lastTPF = (curTime - oldTime) * (1.0f / LWJGL_TIMER_RES);
+ lastFPS = 1.0f / lastTPF;
+ oldTime = curTime;
+ }
+
+ /**
+ * toString
returns the string representation of this timer
+ * in the format:
+ *
+ * jme.utility.Timer@1db699b
+ * Time: {LONG}
+ * FPS: {LONG}
+ *
+ * @return the string representation of this object.
+ */
+ @Override
+ public String toString() {
+ String string = super.toString();
+ string += "\nTime: " + oldTime;
+ string += "\nFPS: " + getFrameRate();
+ return string;
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..c089bf28d
--- /dev/null
+++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java
@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) 2009-2012 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.system.lwjgl;
+
+import com.jme3.input.JoyInput;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.TouchInput;
+import com.jme3.input.lwjgl.GlfwJoystickInput;
+import com.jme3.input.lwjgl.LwjglKeyInput;
+import com.jme3.input.lwjgl.LwjglMouseInput;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeContext;
+import com.jme3.system.JmeSystem;
+import org.lwjgl.PointerBuffer;
+import org.lwjgl.Sys;
+import org.lwjgl.glfw.*;
+
+import java.awt.*;
+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 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.
+ *
+ * @author Daniel Johansson (dannyjo)
+ * @since 3.1
+ */
+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 needRestart = new AtomicBoolean(false);
+ protected boolean wasActive = false;
+ protected boolean autoFlush = true;
+ protected boolean allowSwapBuffers = false;
+ private long window = -1;
+ private final JmeContext.Type type;
+
+ private GLFWErrorCallback errorCallback;
+ private GLFWWindowSizeCallback windowSizeCallback;
+ private GLFWWindowFocusCallback windowFocusCallback;
+
+ 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");
+ }
+
+ this.type = type;
+ }
+
+ /**
+ * @return Type.Display or Type.Canvas
+ */
+ public JmeContext.Type getType() {
+ return type;
+ }
+
+ /**
+ * Set the title if its a windowed display
+ *
+ * @param title
+ */
+ public void setTitle(final String title) {
+ if (created.get() && window != -1) {
+ glfwSetWindowTitle(window, title);
+ }
+ }
+
+ /**
+ * Restart if its a windowed or full-screen display.
+ */
+ public void restart() {
+ if (created.get()) {
+ needRestart.set(true);
+ } else {
+ LOGGER.warning("Display is not created, cannot restart window.");
+ }
+ }
+
+ /**
+ * Apply the settings, changing resolution, etc.
+ *
+ * @param settings
+ */
+ protected void createContext(final AppSettings settings) {
+ glfwSetErrorCallback(errorCallback = new GLFWErrorCallback() {
+ @Override
+ public void invoke(int error, long description) {
+ final String message = Callbacks.errorCallbackDescriptionString(description);
+ listener.handleError(message, new Exception(message));
+ }
+ });
+
+ if (glfwInit() != GL_TRUE) {
+ throw new IllegalStateException("Unable to initialize GLFW");
+ }
+
+ glfwDefaultWindowHints();
+ glfwWindowHint(GLFW_VISIBLE, GL_FALSE);
+
+ // TODO: Add support for monitor selection
+ long monitor = NULL;
+
+ if (settings.isFullscreen()) {
+ monitor = glfwGetPrimaryMonitor();
+ }
+
+ final ByteBuffer videoMode = glfwGetVideoMode(glfwGetPrimaryMonitor());
+
+ 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 (window == NULL) {
+ throw new RuntimeException("Failed to create the GLFW window");
+ }
+
+ glfwWindowHint(GLFW_RESIZABLE, settings.isResizable() ? GL_TRUE : GL_FALSE);
+ glfwWindowHint(GLFW_DEPTH_BITS, settings.getDepthBits());
+ glfwWindowHint(GLFW_STENCIL_BITS, settings.getStencilBits());
+ glfwWindowHint(GLFW_SAMPLES, settings.getSamples());
+ glfwWindowHint(GLFW_STEREO, settings.useStereo3D() ? GL_TRUE : GL_FALSE);
+
+ int frameRateCap = settings.getFrameRate();
+
+ if (!autoFlush) {
+ frameRateCap = 20;
+ }
+
+ if (frameRateCap > 0) {
+ glfwWindowHint(GLFW_REFRESH_RATE, frameRateCap);
+ }
+
+ // Not sure how else to support bits per pixel
+ if (settings.getBitsPerPixel() == 24) {
+ glfwWindowHint(GLFW_RED_BITS, 8);
+ glfwWindowHint(GLFW_GREEN_BITS, 8);
+ glfwWindowHint(GLFW_BLUE_BITS, 8);
+ } else if (settings.getBitsPerPixel() == 16) {
+ glfwWindowHint(GLFW_RED_BITS, 5);
+ glfwWindowHint(GLFW_GREEN_BITS, 6);
+ glfwWindowHint(GLFW_BLUE_BITS, 5);
+ }
+
+ glfwWindowHint(GLFW_ALPHA_BITS, settings.getAlphaBits());
+
+ glfwSetWindowFocusCallback(window, windowFocusCallback = new GLFWWindowFocusCallback() {
+ @Override
+ public void invoke(final long window, final int focused) {
+ final boolean focus = (focused == 1);
+
+ if (wasActive != focus) {
+ if (!wasActive) {
+ listener.gainFocus();
+ timer.reset();
+ } else {
+ listener.loseFocus();
+ }
+
+ wasActive = !wasActive;
+ }
+
+ }
+ });
+
+ // Center the window
+ if (!settings.isFullscreen() && Type.Display.equals(type)) {
+ glfwSetWindowPos(window, (GLFWvidmode.width(videoMode) - settings.getWidth()) / 2, (GLFWvidmode.height(videoMode) - settings.getHeight()) / 2);
+ }
+
+ // Make the OpenGL context current
+ glfwMakeContextCurrent(window);
+
+ // Enable vsync
+ if (settings.isVSync()) {
+ glfwSwapInterval(1);
+ } else {
+ glfwSwapInterval(0);
+ }
+
+
+ // Make the window visible
+ if (Type.Display.equals(type)) {
+ glfwShowWindow(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);
+ }
+ });
+
+ allowSwapBuffers = settings.isSwapBuffers();
+
+ // TODO: When GLFW 3.2 is released and included in LWJGL 3.x then we should hopefully be able to set the window icon.
+ }
+
+ /**
+ * Destroy the context.
+ */
+ protected void destroyContext() {
+ try {
+ if (renderer != null) {
+ renderer.cleanup();
+ }
+
+ errorCallback.release();
+ windowSizeCallback.release();
+ windowFocusCallback.release();
+
+ if (window != 0) {
+ glfwDestroyWindow(window);
+ }
+ //glfwTerminate();
+ } catch (Exception ex) {
+ listener.handleError("Failed to destroy context", ex);
+ }
+ }
+
+ public void create(boolean waitFor) {
+ if (created.get()) {
+ LOGGER.warning("create() called when display is already created!");
+ return;
+ }
+
+ new Thread(this, THREAD_NAME).start();
+ if (waitFor)
+ waitFor(true);
+ }
+
+ /**
+ * Does LWJGL display initialization in the OpenGL thread
+ */
+ protected boolean initInThread() {
+ try {
+ if (!JmeSystem.isLowPermissions()) {
+ // Enable uncaught exception handler only for current thread
+ Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+ public void uncaughtException(Thread thread, Throwable thrown) {
+ listener.handleError("Uncaught exception thrown in " + thread.toString(), thrown);
+ if (needClose.get()) {
+ // listener.handleError() has requested the
+ // context to close. Satisfy request.
+ deinitInThread();
+ }
+ }
+ });
+ }
+
+ timer = new LwjglTimer();
+
+ // For canvas, this will create a pbuffer,
+ // allowing us to query information.
+ // When the canvas context becomes available, it will
+ // be replaced seamlessly.
+ createContext(settings);
+ printContextInitInfo();
+
+ created.set(true);
+ super.internalCreate();
+ } catch (Exception ex) {
+ try {
+ if (window != -1) {
+ //glfwSetWindowShouldClose(window, GL_TRUE);
+ glfwDestroyWindow(window);
+ }
+ } catch (Exception ex2) {
+ LOGGER.log(Level.WARNING, null, ex2);
+ }
+
+ listener.handleError("Failed to create display", ex);
+ return false; // if we failed to create display, do not continue
+ }
+
+ listener.initialize();
+ return true;
+ }
+
+ /**
+ * execute one iteration of the render loop in the OpenGL thread
+ */
+ protected void runLoop() {
+ // If a restart is required, lets recreate the context.
+ if (needRestart.getAndSet(false)) {
+ try {
+ createContext(settings);
+ } catch (Exception ex) {
+ LOGGER.log(Level.SEVERE, "Failed to set display settings!", ex);
+ }
+
+ LOGGER.fine("Display restarted.");
+ }
+
+ if (!created.get()) {
+ throw new IllegalStateException();
+ }
+
+ listener.update();
+
+ // All this does is call swap buffers
+ // If the canvas is not active, there's no need to waste time
+ // doing that ..
+ if (renderable.get()) {
+ // calls swap buffers, etc.
+ try {
+ if (allowSwapBuffers && autoFlush) {
+ glfwSwapBuffers(window);
+ }
+ } catch (Throwable ex) {
+ listener.handleError("Error while swapping buffers", ex);
+ }
+ }
+
+ if (glfwGetWindowAttrib(window, GLFW_FOCUSED) == GL_TRUE) {
+ glfwPollEvents();
+ }
+
+ // Subclasses just call GLObjectManager clean up objects here
+ // it is safe .. for now.
+ if (renderer != null) {
+ renderer.postFrame();
+ }
+ }
+
+ /**
+ * De-initialize in the OpenGL thread.
+ */
+ protected void deinitInThread() {
+ destroyContext();
+
+ listener.destroy();
+ LOGGER.fine("Display destroyed.");
+ super.internalDestroy();
+ }
+
+ public void run() {
+ if (listener == null) {
+ throw new IllegalStateException("SystemListener is not set on context!"
+ + "Must set with JmeContext.setSystemListner().");
+ }
+
+ registerNatives();
+ loadNatives();
+ LOGGER.log(Level.FINE, "Using LWJGL {0}", Sys.getVersion());
+
+ if (!initInThread()) {
+ LOGGER.log(Level.SEVERE, "Display initialization failed. Cannot continue.");
+ return;
+ }
+
+ while (true) {
+ if (glfwWindowShouldClose(window) == GL_TRUE) {
+ listener.requestClose(false);
+ }
+
+ runLoop();
+
+ if (needClose.get()) {
+ break;
+ }
+ }
+
+ deinitInThread();
+ }
+
+ public JoyInput getJoyInput() {
+ if (joyInput == null) {
+ joyInput = new GlfwJoystickInput();
+ }
+ return joyInput;
+ }
+
+ public MouseInput getMouseInput() {
+ if (mouseInput == null) {
+ mouseInput = new LwjglMouseInput(this);
+ }
+ return mouseInput;
+ }
+
+ public KeyInput getKeyInput() {
+ if (keyInput == null) {
+ keyInput = new LwjglKeyInput(this);
+ }
+
+ return keyInput;
+ }
+
+ public TouchInput getTouchInput() {
+ return null;
+ }
+
+ public void setAutoFlushFrames(boolean enabled) {
+ this.autoFlush = enabled;
+ }
+
+ public void destroy(boolean waitFor) {
+ needClose.set(true);
+
+ if (waitFor) {
+ waitFor(false);
+ }
+ }
+
+ public long getWindowHandle() {
+ return window;
+ }
+
+ 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);
+ }
+}
diff --git a/settings.gradle b/settings.gradle
index 89b8fc075..8b9000340 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -15,6 +15,7 @@ include 'jme3-desktop'
include 'jme3-blender'
include 'jme3-jogl'
include 'jme3-lwjgl'
+include 'jme3-lwjgl3'
// Other external dependencies
include 'jme3-jbullet'