diff --git a/engine/build.gradle b/engine/build.gradle index cb528ef8b..d0ae86290 100644 --- a/engine/build.gradle +++ b/engine/build.gradle @@ -105,6 +105,7 @@ dependencies { compile group: 'org.lwjgl.lwjgl', name: 'lwjgl', version: LwjglVersion compile group: 'org.lwjgl.lwjgl', name: 'lwjgl_util', version: LwjglVersion compile group: 'java3d', name: 'vecmath', version: '1.3.1' // Note: Downgraded to this release until TeraMath ready + compile "org.joml:joml:1.8.1" compile group: 'org.abego.treelayout', name: 'org.abego.treelayout.core', version: '1.0.3' compile group: 'com.miglayout', name: 'miglayout-core', version: '5.0' compile group: 'de.matthiasmann.twl', name: 'PNGDecoder', version: '1111' diff --git a/engine/src/main/java/org/terasology/rendering/openvrprovider/ControllerListener.java b/engine/src/main/java/org/terasology/rendering/openvrprovider/ControllerListener.java new file mode 100644 index 000000000..d8ae919d9 --- /dev/null +++ b/engine/src/main/java/org/terasology/rendering/openvrprovider/ControllerListener.java @@ -0,0 +1,20 @@ +package openvrprovider; + +import jopenvr.JOpenVRLibrary; +import jopenvr.VRControllerState_t; + +/* Interface intended to be front-facing to user for controller interaction. */ +public interface ControllerListener { + public final int LEFT_CONTROLLER = 0; + public final int RIGHT_CONTROLLER = 1; + public static int k_EAxis_Trigger = 1; + public static int k_EAxis_TouchPad = 0; + public static long k_buttonTouchpad = (1L << JOpenVRLibrary.EVRButtonId.EVRButtonId_k_EButton_SteamVR_Touchpad); + public static long k_buttonTrigger = (1L << JOpenVRLibrary.EVRButtonId.EVRButtonId_k_EButton_SteamVR_Trigger); + public static long k_buttonAppMenu = (1L << JOpenVRLibrary.EVRButtonId.EVRButtonId_k_EButton_ApplicationMenu); + public static long k_buttonGrip = (1L << JOpenVRLibrary.EVRButtonId.EVRButtonId_k_EButton_Grip); + public static float triggerThreshold = .25f; + + public void buttonStateChanged(VRControllerState_t stateBefore, VRControllerState_t stateAfter, int nController); + // TODO: touch, axes +} diff --git a/engine/src/main/java/org/terasology/rendering/openvrprovider/OSValidator.java b/engine/src/main/java/org/terasology/rendering/openvrprovider/OSValidator.java new file mode 100644 index 000000000..137ab3312 --- /dev/null +++ b/engine/src/main/java/org/terasology/rendering/openvrprovider/OSValidator.java @@ -0,0 +1,59 @@ +package openvrprovider; + +/* Mainly used to locate the OpenVR library on different platforms. */ +public class OSValidator { + private static final String OS = System.getProperty("os.name").toLowerCase(); + private static final String arch = System.getProperty("os.arch"); + private static final String userDir = System.getProperty("user.dir"); + + public static boolean isWin64() { + + return (OS.indexOf("win") >= 0 && arch.contains("64")); + + } + + public static boolean isWin32() { + + return (OS.indexOf("win") >= 0 && arch.contains("32")); + + } + + public static boolean isLinux64() { + + return (OS.indexOf("linux") >= 0 && arch.contains("64")); + + } + + public static boolean isLinux32() { + + return (OS.indexOf("linux") >= 0 && arch.contains("32")); + + } + + public static boolean isMac() { + + return (OS.indexOf("mac") >= 0); + + } + + public static String getOsString() { + if (isWin64()) + return new String("win32-x86-64"); + else if (isWin32()) + return new String("win32-x86"); + else if (isLinux64()) + return new String("linux-x86-64"); + else if (isLinux32()) + return new String("linux-x86"); + else if (isMac()) + return new String("darwin"); + return new String("unknown"); + } + + public static String getLibPath() { + if (getOsString().contains("win")) + return userDir + "\\openvr_natives\\" + getOsString(); + return userDir + "/openvr_natives/" + getOsString(); + } + +} diff --git a/engine/src/main/java/org/terasology/rendering/openvrprovider/OpenVRProvider.java b/engine/src/main/java/org/terasology/rendering/openvrprovider/OpenVRProvider.java new file mode 100644 index 000000000..7d8d6b1af --- /dev/null +++ b/engine/src/main/java/org/terasology/rendering/openvrprovider/OpenVRProvider.java @@ -0,0 +1,332 @@ +package openvrprovider; + +import com.sun.jna.Memory; +import com.sun.jna.NativeLibrary; +import com.sun.jna.Pointer; +import jopenvr.*; +import jopenvr.JOpenVRLibrary.EVREventType; + +import java.nio.IntBuffer; + +import static openvrprovider.ControllerListener.LEFT_CONTROLLER; +import static openvrprovider.ControllerListener.RIGHT_CONTROLLER; + +/* This class is designed to make all API calls to OpenVR, thereby insulating it from the user. If you're looking to get + * some information from the headset/controllers you should probably look at OpenVRStereoRenderer, ControllerListener, + * or OpenVRState + * */ +public class OpenVRProvider { + private static boolean initialized = false; + private static VR_IVRSystem_FnTable vrsystem; + private static VR_IVRCompositor_FnTable vrCompositor; + private static VR_IVROverlay_FnTable vrOverlay; + private static VR_IVRSettings_FnTable vrSettings; + public OpenVRState vrState = new OpenVRState(); + private static int[] controllerDeviceIndex = new int[2]; + private static VRControllerState_t.ByReference[] inputStateRefernceArray = new VRControllerState_t.ByReference[2]; + private static VRControllerState_t[] controllerStateReference = new VRControllerState_t[2]; + private static IntBuffer hmdErrorStore; + private static TrackedDevicePose_t.ByReference hmdTrackedDevicePoseReference; + private static TrackedDevicePose_t[] hmdTrackedDevicePoses; + // TextureIDs of framebuffers for each eye + private float nearClip = 0.5f; + private float farClip = 500.0f; + private final static VRTextureBounds_t texBounds = new VRTextureBounds_t(); + public static Texture_t texType[] = new Texture_t[2]; + private static boolean[] controllerTracking = new boolean[2]; + //keyboard + private static boolean keyboardShowing = false; + private static boolean headIsTracking = false; + + public boolean init() { + try { + if (!initializeOpenVRLibrary()) + return false; + initializeJOpenVR(); + initOpenVRCompositor(true); + initOpenVROverlay(); + initOpenVROSettings(); + } catch (Exception e) { + return false; + } + initialized = true; + return true; + } + + public void shutdown() { + initialized = false; + } + + public void updateState() + { + if (!initialized) + init(); + updatePose(); + pollControllers(); + pollInputEvents(); + } + + private void pollControllers() { + for (int c = 0; c < 2; c++) { + if (controllerDeviceIndex[c] != -1) { + vrsystem.GetControllerState.apply(controllerDeviceIndex[c], inputStateRefernceArray[c]); + inputStateRefernceArray[c].read(); + controllerStateReference[c] = inputStateRefernceArray[c]; + vrState.updateControllerButtonState(controllerStateReference); + } + } + } + + private boolean initializeOpenVRLibrary() throws Exception { + if (initialized) + return true; + System.out.println("Adding OpenVR search path: " + OSValidator.getLibPath()); + NativeLibrary.addSearchPath("openvr_api", OSValidator.getLibPath()); + + if (jopenvr.JOpenVRLibrary.VR_IsHmdPresent() == 1) { + System.out.println("VR Headset detected."); + } else { + System.out.println("VR Headset not detected."); + return false; + } + return true; + } + + public OpenVRProvider() { + for (int c = 0; c < 2; c++) { + controllerDeviceIndex[c] = -1; + controllerStateReference[c] = new VRControllerState_t(); + inputStateRefernceArray[c] = new VRControllerState_t.ByReference(); + inputStateRefernceArray[c].setAutoRead(false); + inputStateRefernceArray[c].setAutoWrite(false); + inputStateRefernceArray[c].setAutoSynch(false); + texType[c] = new Texture_t(); + } + } + + private static void initializeJOpenVR() throws Exception { + hmdErrorStore = IntBuffer.allocate(1); + vrsystem = null; + JOpenVRLibrary.VR_InitInternal(hmdErrorStore, JOpenVRLibrary.EVRApplicationType.EVRApplicationType_VRApplication_Scene); + if (hmdErrorStore.get(0) == 0) { + // ok, try and get the vrsystem pointer.. + vrsystem = new VR_IVRSystem_FnTable(JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRSystem_Version, hmdErrorStore)); + } + if (vrsystem == null || hmdErrorStore.get(0) != 0) { + throw new Exception(jopenvr.JOpenVRLibrary.VR_GetVRInitErrorAsEnglishDescription(hmdErrorStore.get(0)).getString(0)); + } else { + + vrsystem.setAutoSynch(false); + vrsystem.read(); + + System.out.println("OpenVR initialized & VR connected."); + + hmdTrackedDevicePoseReference = new TrackedDevicePose_t.ByReference(); + hmdTrackedDevicePoses = (TrackedDevicePose_t[]) hmdTrackedDevicePoseReference.toArray(JOpenVRLibrary.k_unMaxTrackedDeviceCount); + + // disable all this stuff which kills performance + hmdTrackedDevicePoseReference.setAutoRead(false); + hmdTrackedDevicePoseReference.setAutoWrite(false); + hmdTrackedDevicePoseReference.setAutoSynch(false); + for (int i = 0; i < JOpenVRLibrary.k_unMaxTrackedDeviceCount; i++) { + hmdTrackedDevicePoses[i].setAutoRead(false); + hmdTrackedDevicePoses[i].setAutoWrite(false); + hmdTrackedDevicePoses[i].setAutoSynch(false); + } + } + } + + // needed for in-game keyboard + private static void initOpenVROverlay() throws Exception { + vrOverlay = new VR_IVROverlay_FnTable(JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVROverlay_Version, hmdErrorStore)); + if (hmdErrorStore.get(0) == 0) { + vrOverlay.setAutoSynch(false); + vrOverlay.read(); + System.out.println("OpenVR Overlay initialized OK"); + } else { + throw new Exception(jopenvr.JOpenVRLibrary.VR_GetVRInitErrorAsEnglishDescription(hmdErrorStore.get(0)).getString(0)); + } + } + + private static void initOpenVROSettings() throws Exception { + vrSettings = new VR_IVRSettings_FnTable(JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRSettings_Version, hmdErrorStore)); + if (hmdErrorStore.get(0) == 0) { + vrSettings.setAutoSynch(false); + vrSettings.read(); + System.out.println("OpenVR Settings initialized OK"); + } else { + throw new Exception(jopenvr.JOpenVRLibrary.VR_GetVRInitErrorAsEnglishDescription(hmdErrorStore.get(0)).getString(0)); + } + } + + private static void initOpenVRCompositor(boolean set) throws Exception { + if (set && vrsystem != null) { + vrCompositor = new VR_IVRCompositor_FnTable(JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRCompositor_Version, hmdErrorStore)); + if (hmdErrorStore.get(0) == 0) { + System.out.println("OpenVR Compositor initialized OK."); + vrCompositor.setAutoSynch(false); + vrCompositor.read(); + vrCompositor.SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding); + } else { + throw new Exception(jopenvr.JOpenVRLibrary.VR_GetVRInitErrorAsEnglishDescription(hmdErrorStore.get(0)).getString(0)); + } + } + if (vrCompositor == null) { + System.out.println("Skipping VR Compositor..."); + } + + // left eye + texBounds.uMax = 1f; + texBounds.uMin = 0f; + texBounds.vMax = 1f; + texBounds.vMin = 0f; + texBounds.setAutoSynch(false); + texBounds.setAutoRead(false); + texBounds.setAutoWrite(false); + texBounds.write(); + // texture type + for (int nEye = 0; nEye < 2; nEye++) { + texType[0].eColorSpace = JOpenVRLibrary.EColorSpace.EColorSpace_ColorSpace_Gamma; + texType[0].eType = JOpenVRLibrary.EGraphicsAPIConvention.EGraphicsAPIConvention_API_OpenGL; + texType[0].setAutoSynch(false); + texType[0].setAutoRead(false); + texType[0].setAutoWrite(false); + texType[0].handle = -1; + texType[0].write(); + + } + System.out.println("OpenVR Compositor initialized OK."); + + } + + public static boolean setKeyboardOverlayShowing(boolean showingState) { + int ret = 1; + if (showingState) { + Pointer pointer = new Memory(3); + pointer.setString(0, "mc"); + Pointer empty = new Memory(1); + empty.setString(0, ""); + + ret = vrOverlay.ShowKeyboard.apply(0, 0, pointer, 256, empty, (byte) 1, 0); + + keyboardShowing = 0 == ret; //0 = no error, > 0 see EVROverlayError + + + if (ret != 0) { + System.out.println("VR Overlay Error: " + vrOverlay.GetOverlayErrorNameFromEnum.apply(ret).getString(0)); + } + + } else { + try { + vrOverlay.HideKeyboard.apply(); + } catch (Error e) { + } + keyboardShowing = false; + } + + return keyboardShowing; + } + + public void destroy() { + if (this.initialized) { + JOpenVRLibrary.VR_ShutdownInternal(); + this.initialized = false; + } + } + + private static void findControllerDevices() { + controllerDeviceIndex[RIGHT_CONTROLLER] = -1; + controllerDeviceIndex[LEFT_CONTROLLER] = -1; + + controllerDeviceIndex[RIGHT_CONTROLLER] = vrsystem.GetTrackedDeviceIndexForControllerRole.apply(JOpenVRLibrary.ETrackedControllerRole.ETrackedControllerRole_TrackedControllerRole_LeftHand); + controllerDeviceIndex[LEFT_CONTROLLER] = vrsystem.GetTrackedDeviceIndexForControllerRole.apply(JOpenVRLibrary.ETrackedControllerRole.ETrackedControllerRole_TrackedControllerRole_RightHand); + } + + //jrbuda:: oh hello there you are. + private static void pollInputEvents() { + if (vrsystem == null) return; + + jopenvr.VREvent_t event = new jopenvr.VREvent_t(); + + while (vrsystem.PollNextEvent.apply(event, event.size()) > 0) { + + switch (event.eventType) { + case EVREventType.EVREventType_VREvent_KeyboardClosed: + //'huzzah' + keyboardShowing = false; + break; + case EVREventType.EVREventType_VREvent_KeyboardCharInput: + byte[] inbytes = event.data.getPointer().getByteArray(0, 8); + int len = 0; + for (byte b : inbytes) { + if (b > 0) len++; + } + break; + default: + break; + } + } + } + + private void updatePose() { + if (vrsystem == null || vrCompositor == null) + return; + + vrCompositor.WaitGetPoses.apply(hmdTrackedDevicePoseReference, JOpenVRLibrary.k_unMaxTrackedDeviceCount, null, 0); + for (int nDevice = 0; nDevice < JOpenVRLibrary.k_unMaxTrackedDeviceCount; ++nDevice) { + hmdTrackedDevicePoses[nDevice].read(); + } + + if (hmdTrackedDevicePoses[JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd].bPoseIsValid != 0) { + for (int nEye = 0; nEye < 2; nEye++) { + HmdMatrix34_t matPose = vrsystem.GetEyeToHeadTransform.apply(nEye); + vrState.setEyePoseWRTHead(matPose, nEye); + HmdMatrix44_t matProjection = vrsystem.GetProjectionMatrix.apply(nEye, nearClip, farClip, JOpenVRLibrary.EGraphicsAPIConvention.EGraphicsAPIConvention_API_OpenGL); + vrState.setProjectionMatrix(matProjection, nEye); + } + vrState.setHeadPose(hmdTrackedDevicePoses[JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking); + headIsTracking = true; + } else { + headIsTracking = false; + } + + findControllerDevices(); + + for (int c = 0; c < 2; c++) { + if (controllerDeviceIndex[c] != -1) { + controllerTracking[c] = true; + vrState.setControllerPose(hmdTrackedDevicePoses[controllerDeviceIndex[c]].mDeviceToAbsoluteTracking, c); + } else { + controllerTracking[c] = false; + } + } + } + + public static void triggerHapticPulse(int controller, int strength) { + if (controllerDeviceIndex[controller] == -1) + return; + vrsystem.TriggerHapticPulse.apply(controllerDeviceIndex[controller], 0, (short) strength); + } + + public void submitFrame() { + if (vrCompositor == null) return; + if (vrCompositor.Submit == null) return; + for (int nEye = 0; nEye < 2; nEye++) { + int ret = vrCompositor.Submit.apply( + nEye, + texType[nEye], null, + JOpenVRLibrary.EVRSubmitFlags.EVRSubmitFlags_Submit_Default); + } + if (vrCompositor.PostPresentHandoff != null) + vrCompositor.PostPresentHandoff.apply(); + } + + public void setNearClip(float _nearClip) { + nearClip = _nearClip; + } + + public void setFarClip(float _farClip) { + farClip = _farClip; + } + +} diff --git a/engine/src/main/java/org/terasology/rendering/openvrprovider/OpenVRState.java b/engine/src/main/java/org/terasology/rendering/openvrprovider/OpenVRState.java new file mode 100644 index 000000000..fb3d28af4 --- /dev/null +++ b/engine/src/main/java/org/terasology/rendering/openvrprovider/OpenVRState.java @@ -0,0 +1,99 @@ +package openvrprovider; + +import jopenvr.*; +import org.joml.Matrix4f; + +import java.util.ArrayList; +import java.util.List; + +/* Contains all of the information that the user will need from OpenVR without using any OpenVR data structures. The +OpenVRProvider automatically updates this. + */ +public class OpenVRState { + public static int LEFT_EYE = JOpenVRLibrary.EVREye.EVREye_Eye_Left; + public static int RIGHT_EYE = JOpenVRLibrary.EVREye.EVREye_Eye_Right; + + private List controllerListeners = new ArrayList<>(); + + public void addControllerListener(ControllerListener toAdd) { + controllerListeners.add(toAdd); + } + + // In the head frame + private Matrix4f[] eyePoses = new Matrix4f[2]; + private Matrix4f[] projectionMatrices = new Matrix4f[2]; + + // In the tracking system intertial frame + private Matrix4f headPose = new Matrix4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + + // Controllers + private static Matrix4f[] controllerPose = new Matrix4f[2]; + private static VRControllerState_t[] lastControllerState = new VRControllerState_t[2]; + + OpenVRState() { + for (int c = 0; c < 2; c++) { + lastControllerState[c] = new VRControllerState_t(); + controllerPose[c] = new Matrix4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + eyePoses[c] = new Matrix4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + projectionMatrices[c] = new Matrix4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + + for (int i = 0; i < 5; i++) { + lastControllerState[c].rAxis[i] = new VRControllerAxis_t(); + } + } + + } + + public void setHeadPose(HmdMatrix34_t inputPose) { + OpenVRUtil.setSteamVRMatrix3ToMatrix4f(inputPose, headPose); + } + + public void setEyePoseWRTHead(HmdMatrix34_t inputPose, int nIndex) { + OpenVRUtil.setSteamVRMatrix3ToMatrix4f(inputPose, eyePoses[nIndex]); + } + + public void setControllerPose(HmdMatrix34_t inputPose, int nIndex) { + OpenVRUtil.setSteamVRMatrix3ToMatrix4f(inputPose, controllerPose[nIndex]); + } + + public Matrix4f getEyePose(int nEye) { + Matrix4f matrixReturn = new Matrix4f(headPose); + matrixReturn.mul(eyePoses[nEye]); + return matrixReturn; + } + + public Matrix4f getEyeProjectionMatrix(int nEye) { + return new Matrix4f(projectionMatrices[nEye]); + } + + public void updateControllerButtonState( + VRControllerState_t[] controllerStateReference) { + for (int c = 0; c < 2; c++) //each controller + { + // store previous state + if (lastControllerState[c].ulButtonPressed != controllerStateReference[c].ulButtonPressed) { + for (ControllerListener listener : controllerListeners) { + listener.buttonStateChanged(lastControllerState[c], controllerStateReference[c], c); + } + } + lastControllerState[c].unPacketNum = controllerStateReference[c].unPacketNum; + lastControllerState[c].ulButtonPressed = controllerStateReference[c].ulButtonPressed; + lastControllerState[c].ulButtonTouched = controllerStateReference[c].ulButtonTouched; + + for (int i = 0; i < 5; i++) //5 axes but only [0] and [1] is anything, trigger and touchpad + { + if (controllerStateReference[c].rAxis[i] != null) { + lastControllerState[c].rAxis[i].x = controllerStateReference[c].rAxis[i].x; + lastControllerState[c].rAxis[i].y = controllerStateReference[c].rAxis[i].y; + } + } + } + } + + public void setProjectionMatrix( + HmdMatrix44_t inputPose, + int nEye) { + OpenVRUtil.setSteamVRMatrix44ToMatrix4f(inputPose, projectionMatrices[nEye]); + } + +} diff --git a/engine/src/main/java/org/terasology/rendering/openvrprovider/OpenVRStereoRenderer.java b/engine/src/main/java/org/terasology/rendering/openvrprovider/OpenVRStereoRenderer.java new file mode 100644 index 000000000..83fc2657d --- /dev/null +++ b/engine/src/main/java/org/terasology/rendering/openvrprovider/OpenVRStereoRenderer.java @@ -0,0 +1,71 @@ +package openvrprovider; + +import jopenvr.JOpenVRLibrary; +import org.lwjgl.opengl.EXTFramebufferObject; +import org.lwjgl.opengl.GL11; + +import static org.lwjgl.opengl.EXTFramebufferObject.*; +import static org.lwjgl.opengl.GL11.GL_DEPTH_COMPONENT; +import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D; + +/* This class is designed to manage the framebuffers for the headset. */ +public class OpenVRStereoRenderer { + public OpenVRProvider vrProvider; + + // TextureIDs of framebuffers for each eye + private int eyeTextureIDs[] = new int[2]; + private int eyeFBOIds[] = new int[2]; + + public OpenVRStereoRenderer(OpenVRProvider _vrProvider, int lwidth, int lheight) { + vrProvider = _vrProvider; + createRenderTexture(lwidth, lheight); + } + + public void deleteRenderTextures() { + if (eyeTextureIDs[0] > 0) GL11.glDeleteTextures(eyeTextureIDs[0]); + } + + public void createRenderTexture(int lwidth, int lheight) { + for (int nEye = 0; nEye < 2; nEye++) { + eyeFBOIds[nEye] = glGenFramebuffersEXT(); + EXTFramebufferObject.glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, eyeFBOIds[nEye]); + + //depth buffer + int depthbuffer = glGenRenderbuffersEXT(); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer); + + //allocate space for the renderbuffer + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, lwidth, lheight); + + //attach depth buffer to fbo + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthbuffer); + + eyeTextureIDs[nEye] = GL11.glGenTextures(); + int boundTextureId = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D); + GL11.glBindTexture(GL_TEXTURE_2D, eyeTextureIDs[nEye]); + GL11.glEnable(GL_TEXTURE_2D); + GL11.glTexParameterf(GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); + GL11.glTexParameterf(GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); + GL11.glTexImage2D(GL_TEXTURE_2D, 0, GL11.GL_RGBA8, lwidth, lheight, 0, GL11.GL_RGBA, GL11.GL_INT, (java.nio.ByteBuffer) null); + GL11.glBindTexture(GL_TEXTURE_2D, boundTextureId); + + //attach texture to the fbo + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, eyeTextureIDs[nEye], 0); + + //check completeness + if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) { + System.out.println("An error occured creating the frame buffer."); + } + + vrProvider.texType[nEye].handle = eyeTextureIDs[nEye]; + vrProvider.texType[nEye].eColorSpace = JOpenVRLibrary.EColorSpace.EColorSpace_ColorSpace_Gamma; + vrProvider.texType[nEye].eType = JOpenVRLibrary.EGraphicsAPIConvention.EGraphicsAPIConvention_API_OpenGL; + vrProvider.texType[nEye].write(); + } + } + + public int getTextureHandleForEyeFramebuffer(int nEye) { + return eyeFBOIds[nEye]; + } + +} diff --git a/engine/src/main/java/org/terasology/rendering/openvrprovider/OpenVRUtil.java b/engine/src/main/java/org/terasology/rendering/openvrprovider/OpenVRUtil.java new file mode 100644 index 000000000..1e88830a6 --- /dev/null +++ b/engine/src/main/java/org/terasology/rendering/openvrprovider/OpenVRUtil.java @@ -0,0 +1,93 @@ +package openvrprovider; + +import jopenvr.HmdMatrix34_t; +import jopenvr.HmdMatrix44_t; +import jopenvr.VRControllerState_t; +import org.joml.Matrix4f; + +import java.util.concurrent.TimeUnit; + +/* Utility functions that don't interact with the headset (conversions and the like) */ +public class OpenVRUtil { + + private static final long SLEEP_PRECISION = TimeUnit.MILLISECONDS.toNanos(4); + private static final long SPIN_YIELD_PRECISION = TimeUnit.MILLISECONDS.toNanos(2); + + public static Matrix4f convertSteamVRMatrix3ToMatrix4f(HmdMatrix34_t hmdMatrix) { + return new Matrix4f( + hmdMatrix.m[0], hmdMatrix.m[4], hmdMatrix.m[8], 0, + hmdMatrix.m[1], hmdMatrix.m[5], hmdMatrix.m[9], 0, + hmdMatrix.m[2], hmdMatrix.m[6], hmdMatrix.m[10], 0, + hmdMatrix.m[3], hmdMatrix.m[7], hmdMatrix.m[11], 1f + ); + } + + public static Matrix4f convertSteamVRMatrix44ToMatrix4f(HmdMatrix44_t hmdMatrix) { + return new Matrix4f( + hmdMatrix.m[0], hmdMatrix.m[4], hmdMatrix.m[8], hmdMatrix.m[12], + hmdMatrix.m[1], hmdMatrix.m[5], hmdMatrix.m[9], hmdMatrix.m[13], + hmdMatrix.m[2], hmdMatrix.m[6], hmdMatrix.m[10], hmdMatrix.m[14], + hmdMatrix.m[3], hmdMatrix.m[7], hmdMatrix.m[11], hmdMatrix.m[15] + ); + } + + public static void setSteamVRMatrix3ToMatrix4f(HmdMatrix34_t hmdMatrix, Matrix4f matrixToSet) { + matrixToSet.set( + hmdMatrix.m[0], hmdMatrix.m[4], hmdMatrix.m[8], 0, + hmdMatrix.m[1], hmdMatrix.m[5], hmdMatrix.m[9], 0, + hmdMatrix.m[2], hmdMatrix.m[6], hmdMatrix.m[10], 0, + hmdMatrix.m[3], hmdMatrix.m[7], hmdMatrix.m[11], 1f + ); + } + + public static void setSteamVRMatrix44ToMatrix4f(HmdMatrix44_t hmdMatrix, Matrix4f matrixToSet) { + matrixToSet.set( + hmdMatrix.m[0], hmdMatrix.m[4], hmdMatrix.m[8], hmdMatrix.m[12], + hmdMatrix.m[1], hmdMatrix.m[5], hmdMatrix.m[9], hmdMatrix.m[13], + hmdMatrix.m[2], hmdMatrix.m[6], hmdMatrix.m[10], hmdMatrix.m[14], + hmdMatrix.m[3], hmdMatrix.m[7], hmdMatrix.m[11], hmdMatrix.m[15] + ); + } + + public static void sleepNanos(long nanoDuration) { + final long end = System.nanoTime() + nanoDuration; + long timeLeft = nanoDuration; + do { + try { + if (timeLeft > SLEEP_PRECISION) { + Thread.sleep(1); + } else if (timeLeft > SPIN_YIELD_PRECISION) { + Thread.sleep(0); + } + } catch (Exception e) { + } + timeLeft = end - System.nanoTime(); + } while (timeLeft > 0); + } + + public static VRControllerState_t createZeroControllerState() { + VRControllerState_t state = new VRControllerState_t(); + // controller not connected, clear state + state.ulButtonPressed = 0; + + for (int i = 0; i < 5; i++) { + if (state.rAxis[i] != null) { + state.rAxis[i].x = 0.0f; + state.rAxis[i].y = 0.0f; + } + } + return state; + } + + public static boolean isPressed(long nButton, long uiButtonPressed) { + return ((uiButtonPressed & nButton) > 0); + } + + public static boolean switchedDown(long nButton, long stateBefore, long stateAfter) { + return (!isPressed(nButton, stateBefore) && isPressed(nButton, stateAfter)); + } + + public static boolean switchedUp(long nButton, long stateBefore, long stateAfter) { + return (isPressed(nButton, stateBefore) && !isPressed(nButton, stateAfter)); + } +} diff --git a/openvr_natives/darwin/libopenvr_api.dylib b/openvr_natives/darwin/libopenvr_api.dylib new file mode 100644 index 000000000..df781ebe4 Binary files /dev/null and b/openvr_natives/darwin/libopenvr_api.dylib differ diff --git a/openvr_natives/darwin/libopenvr_api.dylib.dSYM/Contents/Info.plist b/openvr_natives/darwin/libopenvr_api.dylib.dSYM/Contents/Info.plist new file mode 100644 index 000000000..60ac3d69b --- /dev/null +++ b/openvr_natives/darwin/libopenvr_api.dylib.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.libopenvr_api.dylib + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/openvr_natives/darwin/libopenvr_api.dylib.dSYM/Contents/Resources/DWARF/libopenvr_api.dylib b/openvr_natives/darwin/libopenvr_api.dylib.dSYM/Contents/Resources/DWARF/libopenvr_api.dylib new file mode 100644 index 000000000..dacf5f8f7 Binary files /dev/null and b/openvr_natives/darwin/libopenvr_api.dylib.dSYM/Contents/Resources/DWARF/libopenvr_api.dylib differ diff --git a/openvr_natives/darwin/libopenvr_api.dylib.dSYM/libopenvr_api.dylib b/openvr_natives/darwin/libopenvr_api.dylib.dSYM/libopenvr_api.dylib new file mode 100644 index 000000000..4255777c9 Binary files /dev/null and b/openvr_natives/darwin/libopenvr_api.dylib.dSYM/libopenvr_api.dylib differ diff --git a/openvr_natives/darwin/libopenvr_api.dylib.dSYM/libopenvr_api.dylib.dSYM/Contents/Info.plist b/openvr_natives/darwin/libopenvr_api.dylib.dSYM/libopenvr_api.dylib.dSYM/Contents/Info.plist new file mode 100644 index 000000000..60ac3d69b --- /dev/null +++ b/openvr_natives/darwin/libopenvr_api.dylib.dSYM/libopenvr_api.dylib.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.libopenvr_api.dylib + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/openvr_natives/darwin/libopenvr_api.dylib.dSYM/libopenvr_api.dylib.dSYM/Contents/Resources/DWARF/libopenvr_api.dylib b/openvr_natives/darwin/libopenvr_api.dylib.dSYM/libopenvr_api.dylib.dSYM/Contents/Resources/DWARF/libopenvr_api.dylib new file mode 100644 index 000000000..cfcfaef9a Binary files /dev/null and b/openvr_natives/darwin/libopenvr_api.dylib.dSYM/libopenvr_api.dylib.dSYM/Contents/Resources/DWARF/libopenvr_api.dylib differ diff --git a/openvr_natives/linux-x86-64/libopenvr_api.so b/openvr_natives/linux-x86-64/libopenvr_api.so new file mode 100644 index 000000000..a190fe10d Binary files /dev/null and b/openvr_natives/linux-x86-64/libopenvr_api.so differ diff --git a/openvr_natives/linux-x86-64/libopenvr_api.so.dbg b/openvr_natives/linux-x86-64/libopenvr_api.so.dbg new file mode 100644 index 000000000..ca9c9af89 Binary files /dev/null and b/openvr_natives/linux-x86-64/libopenvr_api.so.dbg differ diff --git a/openvr_natives/linux-x86/libopenvr_api.so b/openvr_natives/linux-x86/libopenvr_api.so new file mode 100644 index 000000000..a25054508 Binary files /dev/null and b/openvr_natives/linux-x86/libopenvr_api.so differ diff --git a/openvr_natives/linux-x86/libopenvr_api.so.dbg b/openvr_natives/linux-x86/libopenvr_api.so.dbg new file mode 100644 index 000000000..a26ac1c1f Binary files /dev/null and b/openvr_natives/linux-x86/libopenvr_api.so.dbg differ diff --git a/openvr_natives/win32-x86-64/openvr_api.dll b/openvr_natives/win32-x86-64/openvr_api.dll new file mode 100644 index 000000000..dbf1531b4 Binary files /dev/null and b/openvr_natives/win32-x86-64/openvr_api.dll differ diff --git a/openvr_natives/win32-x86-64/openvr_api.pdb b/openvr_natives/win32-x86-64/openvr_api.pdb new file mode 100644 index 000000000..8a2095e9a Binary files /dev/null and b/openvr_natives/win32-x86-64/openvr_api.pdb differ diff --git a/openvr_natives/win32-x86/openvr_api.dll b/openvr_natives/win32-x86/openvr_api.dll new file mode 100644 index 000000000..f845d447e Binary files /dev/null and b/openvr_natives/win32-x86/openvr_api.dll differ diff --git a/openvr_natives/win32-x86/openvr_api.pdb b/openvr_natives/win32-x86/openvr_api.pdb new file mode 100644 index 000000000..a2c9adc39 Binary files /dev/null and b/openvr_natives/win32-x86/openvr_api.pdb differ