|
|
@ -8,10 +8,24 @@ package com.jme3.input.vr; |
|
|
|
import com.jme3.app.VREnvironment; |
|
|
|
import com.jme3.app.VREnvironment; |
|
|
|
import com.jme3.math.*; |
|
|
|
import com.jme3.math.*; |
|
|
|
import com.jme3.renderer.Camera; |
|
|
|
import com.jme3.renderer.Camera; |
|
|
|
|
|
|
|
import com.jme3.util.VRUtil; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import java.nio.IntBuffer; |
|
|
|
|
|
|
|
import java.util.Locale; |
|
|
|
|
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
|
|
|
|
import java.util.logging.Level; |
|
|
|
import java.util.logging.Logger; |
|
|
|
import java.util.logging.Logger; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import static org.lwjgl.opengl.GL11.*; |
|
|
|
|
|
|
|
import static org.lwjgl.ovr.OVR.*; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import static org.lwjgl.ovr.OVRErrorCode.*; |
|
|
|
|
|
|
|
import static org.lwjgl.system.MemoryUtil.*; |
|
|
|
import static org.lwjgl.ovr.OVRUtil.ovr_Detect; |
|
|
|
import static org.lwjgl.ovr.OVRUtil.ovr_Detect; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import org.lwjgl.BufferUtils; |
|
|
|
|
|
|
|
import org.lwjgl.PointerBuffer; |
|
|
|
|
|
|
|
|
|
|
|
import org.lwjgl.ovr.*; |
|
|
|
import org.lwjgl.ovr.*; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
@ -26,6 +40,57 @@ public class OculusVR implements VRAPI { |
|
|
|
private final VREnvironment environment; |
|
|
|
private final VREnvironment environment; |
|
|
|
private boolean initialized; |
|
|
|
private boolean initialized; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Pointer to the HMD object |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private long session; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Information about the VR session (should the app quit, is |
|
|
|
|
|
|
|
* it visible or is the universal menu open, etc) |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private OVRSessionStatus sessionStatus; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* HMD information, such as product name and manufacturer. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private OVRHmdDesc hmdDesc; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* The horizontal resolution of the HMD |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private int resolutionW; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* The vertical resolution of the HMD |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private int resolutionH; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Field-of-view data for each eye (how many degrees from the |
|
|
|
|
|
|
|
* center can the user see). |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private final OVRFovPort fovPorts[] = new OVRFovPort[2]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Data about each eye to be rendered - in particular, the |
|
|
|
|
|
|
|
* offset from the center of the HMD to the eye. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private final OVREyeRenderDesc eyeRenderDesc[] = new OVREyeRenderDesc[2]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Store the projections for each eye, so we don't have to malloc |
|
|
|
|
|
|
|
* and recalculate them each frame. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private final OVRMatrix4f[] projections = new OVRMatrix4f[2]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Store the poses for each eye. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @see #getHMDMatrixPoseLeftEye() |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private final Matrix4f[] eyePoses = new Matrix4f[2]; |
|
|
|
|
|
|
|
|
|
|
|
public OculusVR(VREnvironment environment) { |
|
|
|
public OculusVR(VREnvironment environment) { |
|
|
|
this.environment = environment; |
|
|
|
this.environment = environment; |
|
|
|
} |
|
|
|
} |
|
|
@ -42,23 +107,117 @@ public class OculusVR implements VRAPI { |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public int getDisplayFrequency() { |
|
|
|
public int getDisplayFrequency() { |
|
|
|
throw new UnsupportedOperationException(); |
|
|
|
// TODO find correct frequency. I'm not sure
|
|
|
|
|
|
|
|
// if LibOVR has a way to do that, though.
|
|
|
|
|
|
|
|
return 60; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public boolean initialize() { |
|
|
|
public boolean initialize() { |
|
|
|
OVRDetectResult detect = OVRDetectResult.calloc(); |
|
|
|
OVRDetectResult detect = OVRDetectResult.calloc(); |
|
|
|
ovr_Detect(0, detect); |
|
|
|
ovr_Detect(0, detect); |
|
|
|
System.out.println("OVRDetectResult.IsOculusHMDConnected = " + detect.IsOculusHMDConnected()); |
|
|
|
boolean connected = detect.IsOculusHMDConnected(); |
|
|
|
System.out.println("OVRDetectResult.IsOculusServiceRunning = " + detect.IsOculusServiceRunning()); |
|
|
|
LOGGER.info("OVRDetectResult.IsOculusHMDConnected = " + connected); |
|
|
|
|
|
|
|
LOGGER.info("OVRDetectResult.IsOculusServiceRunning = " + detect.IsOculusServiceRunning()); |
|
|
|
detect.free(); |
|
|
|
detect.free(); |
|
|
|
if (detect.IsOculusHMDConnected() == false) { |
|
|
|
|
|
|
|
|
|
|
|
if (!connected) { |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
initialized = true; |
|
|
|
initialized = true; |
|
|
|
|
|
|
|
|
|
|
|
throw new UnsupportedOperationException("Not yet implemented!"); |
|
|
|
// step 1 - hmd init
|
|
|
|
|
|
|
|
System.out.println("step 1 - hmd init"); |
|
|
|
|
|
|
|
OVRLogCallback callback = new OVRLogCallback() { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void invoke(long userData, int level, long message) { |
|
|
|
|
|
|
|
System.out.println("LibOVR [" + userData + "] [" + level + "] " + memASCII(message)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
OVRInitParams initParams = OVRInitParams.calloc(); |
|
|
|
|
|
|
|
initParams.LogCallback(callback); |
|
|
|
|
|
|
|
//initParams.Flags(ovrInit_Debug);
|
|
|
|
|
|
|
|
if (ovr_Initialize(initParams) != ovrSuccess) { |
|
|
|
|
|
|
|
System.out.println("init failed"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
System.out.println("OVR SDK " + ovr_GetVersionString()); |
|
|
|
|
|
|
|
initParams.free(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// step 2 - hmd create
|
|
|
|
|
|
|
|
System.out.println("step 2 - hmd create"); |
|
|
|
|
|
|
|
PointerBuffer pHmd = memAllocPointer(1); |
|
|
|
|
|
|
|
OVRGraphicsLuid luid = OVRGraphicsLuid.calloc(); |
|
|
|
|
|
|
|
if (ovr_Create(pHmd, luid) != ovrSuccess) { |
|
|
|
|
|
|
|
System.out.println("create failed, try debug"); |
|
|
|
|
|
|
|
//debug headset is now enabled via the Oculus Configuration util . tools -> Service -> Configure
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
session = pHmd.get(0); |
|
|
|
|
|
|
|
memFree(pHmd); |
|
|
|
|
|
|
|
luid.free(); |
|
|
|
|
|
|
|
sessionStatus = OVRSessionStatus.calloc(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// step 3 - hmdDesc queries
|
|
|
|
|
|
|
|
System.out.println("step 3 - hmdDesc queries"); |
|
|
|
|
|
|
|
hmdDesc = OVRHmdDesc.malloc(); |
|
|
|
|
|
|
|
ovr_GetHmdDesc(session, hmdDesc); |
|
|
|
|
|
|
|
System.out.println("ovr_GetHmdDesc = " + hmdDesc.ManufacturerString() + " " + hmdDesc.ProductNameString() + " " + hmdDesc.SerialNumberString() + " " + hmdDesc.Type()); |
|
|
|
|
|
|
|
if (hmdDesc.Type() == ovrHmd_None) { |
|
|
|
|
|
|
|
System.out.println("missing init"); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
resolutionW = hmdDesc.Resolution().w(); |
|
|
|
|
|
|
|
resolutionH = hmdDesc.Resolution().h(); |
|
|
|
|
|
|
|
System.out.println("resolution W=" + resolutionW + ", H=" + resolutionH); |
|
|
|
|
|
|
|
if (resolutionW == 0) { |
|
|
|
|
|
|
|
System.out.println("Huh - width=0"); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// FOV
|
|
|
|
|
|
|
|
for (int eye = 0; eye < 2; eye++) { |
|
|
|
|
|
|
|
fovPorts[eye] = hmdDesc.DefaultEyeFov(eye); |
|
|
|
|
|
|
|
System.out.println("eye " + eye + " = " + fovPorts[eye].UpTan() + ", " + fovPorts[eye].DownTan() + ", " + fovPorts[eye].LeftTan() + ", " + fovPorts[eye].RightTan()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// TODO what does this do? I think it might be the height of the player, for correct floor heights?
|
|
|
|
|
|
|
|
// playerEyePos = new Vector3f(0.0f, -ovr_GetFloat(session, OVR_KEY_EYE_HEIGHT, 1.65f), 0.0f);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// step 4 - tracking - no longer needed as of 0.8.0.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// step 5 - projections
|
|
|
|
|
|
|
|
System.out.println("step 5 - projections"); |
|
|
|
|
|
|
|
for (int eye = 0; eye < 2; eye++) { |
|
|
|
|
|
|
|
projections[eye] = OVRMatrix4f.malloc(); |
|
|
|
|
|
|
|
OVRUtil.ovrMatrix4f_Projection(fovPorts[eye], 0.5f, 500f, OVRUtil.ovrProjection_None, projections[eye]); |
|
|
|
|
|
|
|
//1.3 was right handed, now none flag
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// step 6 - render desc
|
|
|
|
|
|
|
|
System.out.println("step 6 - render desc"); |
|
|
|
|
|
|
|
for (int eye = 0; eye < 2; eye++) { |
|
|
|
|
|
|
|
eyeRenderDesc[eye] = OVREyeRenderDesc.malloc(); |
|
|
|
|
|
|
|
ovr_GetRenderDesc(session, eye, fovPorts[eye], eyeRenderDesc[eye]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Changed from an offset to a pose, so there is also a rotation.
|
|
|
|
|
|
|
|
System.out.println("ipd eye " + eye + " = " + eyeRenderDesc[eye].HmdToEyePose().Position().x()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OVRPosef pose = eyeRenderDesc[eye].HmdToEyePose(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Matrix4f jPose = new Matrix4f(); |
|
|
|
|
|
|
|
jPose.setTranslation(vecO2J(pose.Position(), new Vector3f())); |
|
|
|
|
|
|
|
jPose.setRotationQuaternion(quatO2J(pose.Orientation(), new Quaternion())); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
eyePoses[eye] = jPose; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// step 7 - recenter
|
|
|
|
|
|
|
|
System.out.println("step 7 - recenter"); |
|
|
|
|
|
|
|
ovr_RecenterTrackingOrigin(session); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// throw new UnsupportedOperationException("Not yet implemented!");
|
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
@ -68,14 +227,18 @@ public class OculusVR implements VRAPI { |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void destroy() { |
|
|
|
public void destroy() { |
|
|
|
|
|
|
|
throw new UnsupportedOperationException(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void reset() { |
|
|
|
public void reset() { |
|
|
|
|
|
|
|
throw new UnsupportedOperationException(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void getRenderSize(Vector2f store) { |
|
|
|
public void getRenderSize(Vector2f store) { |
|
|
|
|
|
|
|
store.x = resolutionW; |
|
|
|
|
|
|
|
store.y = resolutionH; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
@ -105,12 +268,12 @@ public class OculusVR implements VRAPI { |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public Matrix4f getHMDMatrixProjectionLeftEye(Camera cam) { |
|
|
|
public Matrix4f getHMDMatrixProjectionLeftEye(Camera cam) { |
|
|
|
throw new UnsupportedOperationException(); |
|
|
|
return matrixO2J(projections[ovrEye_Left], new Matrix4f()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public Matrix4f getHMDMatrixProjectionRightEye(Camera cam) { |
|
|
|
public Matrix4f getHMDMatrixProjectionRightEye(Camera cam) { |
|
|
|
throw new UnsupportedOperationException(); |
|
|
|
return matrixO2J(projections[ovrEye_Right], new Matrix4f()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
@ -130,19 +293,28 @@ public class OculusVR implements VRAPI { |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public Matrix4f getHMDMatrixPoseLeftEye() { |
|
|
|
public Matrix4f getHMDMatrixPoseLeftEye() { |
|
|
|
throw new UnsupportedOperationException(); |
|
|
|
return eyePoses[ovrEye_Left]; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public HmdType getType() { |
|
|
|
public Matrix4f getHMDMatrixPoseRightEye() { |
|
|
|
throw new UnsupportedOperationException(); |
|
|
|
return eyePoses[ovrEye_Left]; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public Matrix4f getHMDMatrixPoseRightEye() { |
|
|
|
public HmdType getType() { |
|
|
|
throw new UnsupportedOperationException(); |
|
|
|
throw new UnsupportedOperationException(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public boolean initVRCompositor(boolean set) { |
|
|
|
|
|
|
|
if (!set) { |
|
|
|
|
|
|
|
throw new UnsupportedOperationException("Cannot use LibOVR without compositor!"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO move initialization code here from VRViewManagerOculus
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void printLatencyInfoToConsole(boolean set) { |
|
|
|
public void printLatencyInfoToConsole(boolean set) { |
|
|
|
throw new UnsupportedOperationException("Not yet implemented!"); |
|
|
|
throw new UnsupportedOperationException("Not yet implemented!"); |
|
|
|
} |
|
|
|
} |
|
|
@ -159,7 +331,62 @@ public class OculusVR implements VRAPI { |
|
|
|
throw new UnsupportedOperationException("Not yet implemented!"); |
|
|
|
throw new UnsupportedOperationException("Not yet implemented!"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public boolean initVRCompositor(boolean set) { |
|
|
|
// UTILITIES
|
|
|
|
throw new UnsupportedOperationException("Not yet implemented!"); |
|
|
|
// TODO move to helper class
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Copy the values from a LibOVR matrix into a jMonkeyEngine matrix. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param from The matrix to copy from. |
|
|
|
|
|
|
|
* @param to The matrix to copy to. |
|
|
|
|
|
|
|
* @return The {@code to} argument. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public static Matrix4f matrixO2J(OVRMatrix4f from, Matrix4f to) { |
|
|
|
|
|
|
|
for (int x = 0; x < 4; x++) { |
|
|
|
|
|
|
|
for (int y = 0; y < 4; y++) { |
|
|
|
|
|
|
|
float val = from.M(x + y * 4); // TODO verify this
|
|
|
|
|
|
|
|
to.set(x, y, val); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return to; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Copy the values from a LibOVR quaternion into a jMonkeyEngine quaternion. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param from The quaternion to copy from. |
|
|
|
|
|
|
|
* @param to The quaternion to copy to. |
|
|
|
|
|
|
|
* @return The {@code to} argument. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public static Quaternion quatO2J(OVRQuatf from, Quaternion to) { |
|
|
|
|
|
|
|
to.set( |
|
|
|
|
|
|
|
from.x(), |
|
|
|
|
|
|
|
from.y(), |
|
|
|
|
|
|
|
from.z(), |
|
|
|
|
|
|
|
from.w() |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return to; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Copy the values from a LibOVR vector into a jMonkeyEngine vector. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param from The vector to copy from. |
|
|
|
|
|
|
|
* @param to The vector to copy to. |
|
|
|
|
|
|
|
* @return The {@code to} argument. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public static Vector3f vecO2J(OVRVector3f from, Vector3f to) { |
|
|
|
|
|
|
|
to.set( |
|
|
|
|
|
|
|
from.x(), |
|
|
|
|
|
|
|
from.y(), |
|
|
|
|
|
|
|
from.z() |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return to; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* vim: set ts=4 softtabstop=0 sw=4 expandtab: */ |
|
|
|
|
|
|
|
|
|
|
|