From 4ce05dd0cad78f5ed5395bfd210ccd237802f53a Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Thu, 28 Sep 2017 12:51:27 +1300 Subject: [PATCH 01/27] Add pre-render method to VRViewManager in preparation for adding Oculus Rift support. See https://hub.jmonkeyengine.org/t/libovr-oculus-rift-support/39427 --- jme3-vr/src/main/java/com/jme3/app/VRAppState.java | 10 ++++++++++ .../main/java/com/jme3/util/AbstractVRViewManager.java | 4 ++++ jme3-vr/src/main/java/com/jme3/util/VRViewManager.java | 6 ++++++ 3 files changed, 20 insertions(+) diff --git a/jme3-vr/src/main/java/com/jme3/app/VRAppState.java b/jme3-vr/src/main/java/com/jme3/app/VRAppState.java index a468e71b9..39df3884b 100644 --- a/jme3-vr/src/main/java/com/jme3/app/VRAppState.java +++ b/jme3-vr/src/main/java/com/jme3/app/VRAppState.java @@ -432,6 +432,16 @@ public class VRAppState extends AbstractAppState { } } + @Override + public void render(RenderManager rm) { + super.render(rm); + + // update compositor + if( environment.getVRViewManager() != null ) { + environment.getVRViewManager().render(); + } + } + @Override public void initialize(AppStateManager stateManager, Application app) { super.initialize(stateManager, app); diff --git a/jme3-vr/src/main/java/com/jme3/util/AbstractVRViewManager.java b/jme3-vr/src/main/java/com/jme3/util/AbstractVRViewManager.java index f4f7e7f91..6c664dd59 100644 --- a/jme3-vr/src/main/java/com/jme3/util/AbstractVRViewManager.java +++ b/jme3-vr/src/main/java/com/jme3/util/AbstractVRViewManager.java @@ -128,6 +128,10 @@ public abstract class AbstractVRViewManager implements VRViewManager { public VREnvironment getVREnvironment(){ return environment; } + + @Override + public void render() { + } /** * Handles moving filters from the main view to each eye diff --git a/jme3-vr/src/main/java/com/jme3/util/VRViewManager.java b/jme3-vr/src/main/java/com/jme3/util/VRViewManager.java index 58a6c166e..1a5eaeb75 100644 --- a/jme3-vr/src/main/java/com/jme3/util/VRViewManager.java +++ b/jme3-vr/src/main/java/com/jme3/util/VRViewManager.java @@ -150,6 +150,12 @@ public interface VRViewManager { */ public void update(float tpf); + /** + * Set up the scene for rendering. + * This method should be called before any rendering takes place. + */ + public void render(); + /** * Send the rendering result as textures to the two eyes. * This method should be called after all the rendering operations From 5fd593980774350e54f18fd1bc07a7e609bb0f84 Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Thu, 28 Sep 2017 13:51:30 +1300 Subject: [PATCH 02/27] Add LWJGL-LibOVR gradle dependency --- jme3-vr/build.gradle | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/jme3-vr/build.gradle b/jme3-vr/build.gradle index ec1ec8fed..d2c299eb0 100644 --- a/jme3-vr/build.gradle +++ b/jme3-vr/build.gradle @@ -2,7 +2,7 @@ if (!hasProperty('mainClass')) { ext.mainClass = '' } -def lwjglVersion = '3.0.0' +def lwjglVersion = '3.1.3' sourceCompatibility = '1.8' @@ -14,4 +14,8 @@ dependencies { // https://mvnrepository.com/artifact/net.java.dev.jna/jna compile group: 'net.java.dev.jna', name: 'jna', version: '4.3.0' compile 'com.nativelibs4java:jnaerator-runtime:0.12' + + // Native LibOVR/Oculus support + compile "org.lwjgl:lwjgl-ovr:${lwjglVersion}" + runtime "org.lwjgl:lwjgl-ovr:${lwjglVersion}:natives-windows" } \ No newline at end of file From d96bf2c13ed36af5e173e18c70fb19aaa9f3955d Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Thu, 28 Sep 2017 15:40:51 +1300 Subject: [PATCH 03/27] Add skeleton OculusVR input --- .../main/java/com/jme3/input/vr/OculusVR.java | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java new file mode 100644 index 000000000..a26060379 --- /dev/null +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java @@ -0,0 +1,165 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.jme3.input.vr; + +import com.jme3.app.VREnvironment; +import com.jme3.math.*; +import com.jme3.renderer.Camera; +import java.util.logging.Logger; + +import static org.lwjgl.ovr.OVRUtil.ovr_Detect; + +import org.lwjgl.ovr.*; + +/** + * Oculus VR (LibOVR 1.3.0) Native support. + * + * @author Campbell Suter + */ +public class OculusVR implements VRAPI { + + private static final Logger LOGGER = Logger.getLogger(OculusVR.class.getName()); + + private final VREnvironment environment; + private boolean initialized; + + public OculusVR(VREnvironment environment) { + this.environment = environment; + } + + @Override + public OpenVRInput getVRinput() { + throw new UnsupportedOperationException(); + } + + @Override + public String getName() { + return "OVR"; + } + + @Override + public int getDisplayFrequency() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean initialize() { + OVRDetectResult detect = OVRDetectResult.calloc(); + ovr_Detect(0, detect); + System.out.println("OVRDetectResult.IsOculusHMDConnected = " + detect.IsOculusHMDConnected()); + System.out.println("OVRDetectResult.IsOculusServiceRunning = " + detect.IsOculusServiceRunning()); + detect.free(); + if (detect.IsOculusHMDConnected() == false) { + return false; + } + + initialized = true; + + throw new UnsupportedOperationException("Not yet implemented!"); + } + + @Override + public boolean isInitialized() { + return initialized; + } + + @Override + public void destroy() { + } + + @Override + public void reset() { + } + + @Override + public void getRenderSize(Vector2f store) { + } + + @Override + public float getInterpupillaryDistance() { + return 0.065f; // TODO + } + + @Override + public Quaternion getOrientation() { + throw new UnsupportedOperationException(); + } + + @Override + public Vector3f getPosition() { + throw new UnsupportedOperationException(); + } + + @Override + public void getPositionAndOrientation(Vector3f storePos, Quaternion storeRot) { + throw new UnsupportedOperationException(); + } + + @Override + public void updatePose() { + throw new UnsupportedOperationException(); + } + + @Override + public Matrix4f getHMDMatrixProjectionLeftEye(Camera cam) { + throw new UnsupportedOperationException(); + } + + @Override + public Matrix4f getHMDMatrixProjectionRightEye(Camera cam) { + throw new UnsupportedOperationException(); + } + + @Override + public Vector3f getHMDVectorPoseLeftEye() { + throw new UnsupportedOperationException(); + } + + @Override + public Vector3f getHMDVectorPoseRightEye() { + throw new UnsupportedOperationException(); + } + + @Override + public Vector3f getSeatedToAbsolutePosition() { + throw new UnsupportedOperationException(); + } + + @Override + public Matrix4f getHMDMatrixPoseLeftEye() { + throw new UnsupportedOperationException(); + } + + @Override + public HmdType getType() { + throw new UnsupportedOperationException(); + } + + @Override + public Matrix4f getHMDMatrixPoseRightEye() { + throw new UnsupportedOperationException(); + } + + public void printLatencyInfoToConsole(boolean set) { + throw new UnsupportedOperationException("Not yet implemented!"); + } + + public void setFlipEyes(boolean set) { + throw new UnsupportedOperationException("Not yet implemented!"); + } + + public Void getCompositor() { + throw new UnsupportedOperationException("Not yet implemented!"); + } + + public Void getVRSystem() { + throw new UnsupportedOperationException("Not yet implemented!"); + } + + public boolean initVRCompositor(boolean set) { + throw new UnsupportedOperationException("Not yet implemented!"); + } +} From cf28e814809dcb59d16b59a792304f48faf57fc2 Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Thu, 28 Sep 2017 12:59:11 +1300 Subject: [PATCH 04/27] Add initial VRViewManagerOculus --- .../com/jme3/util/VRViewManagerOculus.java | 412 ++++++++++++++++++ 1 file changed, 412 insertions(+) create mode 100644 jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java diff --git a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java new file mode 100644 index 000000000..4f61f4dd3 --- /dev/null +++ b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2009-2017 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.util; + +import com.jme3.app.VREnvironment; +import com.jme3.input.vr.VRAPI; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector2f; +import com.jme3.renderer.Camera; +import com.jme3.renderer.ViewPort; +import com.jme3.scene.Spatial; +import com.jme3.texture.*; +import java.nio.IntBuffer; +import java.util.Iterator; +import java.util.logging.Logger; +import org.lwjgl.PointerBuffer; + +import static com.jme3.util.VRViewManager.LEFT_VIEW_NAME; +import static com.jme3.util.VRViewManager.RIGHT_VIEW_NAME; + +import org.lwjgl.ovr.*; + +import static org.lwjgl.BufferUtils.*; +import static org.lwjgl.ovr.OVR.*; +import static org.lwjgl.ovr.OVRErrorCode.*; + +/** + * A rendering system for Oculus's LibOVR API. + * + * @author Campbell Suter + */ +public class VRViewManagerOculus extends AbstractVRViewManager { + + private static final Logger LOG = Logger.getLogger(VRViewManagerOculus.class.getName()); + + private final VREnvironment environment; + + // The size of the texture drawn onto the HMD + private int textureW; + private int textureH; + + long session = 0; // TODO take from OculusVR input + + // Layers to render into + private PointerBuffer layers; + private OVRLayerEyeFov layer0; + + /** + * Chain texture set thing. + */ + long chain; + + /** + * The definitions of the up/down/left/right parts of the FOV for each eye. + */ + private final OVRFovPort fovPorts[] = new OVRFovPort[2]; + + /** + * Frame buffers we can draw into. + */ + private FrameBuffer framebuffers[]; + + public VRViewManagerOculus(VREnvironment environment) { + this.environment = environment; + + if (!environment.compositorAllowed()) { + throw new UnsupportedOperationException("Cannot render without compositor on LibOVR"); + } + } + + @Override + public void initialize() { + setupCamerasAndViews(); + + OVRHmdDesc hmdDesc = null; // TODO take from OculusVR input + + 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() + ); + } + + findHMDTextureSize(); + setupTextureChain(); + setupLayers(); + setupFramebuffers(); + } + + private void findHMDTextureSize() { + OVRFovPort fovPorts[] = null; + + // Texture sizes + float pixelScaling = 1.0f; // pixelsPerDisplayPixel + + OVRSizei leftTextureSize = OVRSizei.malloc(); + ovr_GetFovTextureSize(session, ovrEye_Left, fovPorts[ovrEye_Left], pixelScaling, leftTextureSize); + System.out.println("leftTextureSize W=" + leftTextureSize.w() + ", H=" + leftTextureSize.h()); + + OVRSizei rightTextureSize = OVRSizei.malloc(); + ovr_GetFovTextureSize(session, ovrEye_Right, fovPorts[ovrEye_Right], pixelScaling, rightTextureSize); + System.out.println("rightTextureSize W=" + rightTextureSize.w() + ", H=" + rightTextureSize.h()); + + textureW = leftTextureSize.w() + rightTextureSize.w(); + textureH = Math.max(leftTextureSize.h(), rightTextureSize.h()); + + leftTextureSize.free(); + rightTextureSize.free(); + } + + private PointerBuffer setupTextureChain() { + // Set up the information for the texture buffer chain thing + OVRTextureSwapChainDesc swapChainDesc = OVRTextureSwapChainDesc.calloc() + .Type(ovrTexture_2D) + .ArraySize(1) + .Format(OVR_FORMAT_R8G8B8A8_UNORM_SRGB) + .Width(textureW) + .Height(textureH) + .MipLevels(1) + .SampleCount(1) + .StaticImage(false); // ovrFalse + + // Create the chain + PointerBuffer textureSetPB = createPointerBuffer(1); + if (OVRGL.ovr_CreateTextureSwapChainGL(session, swapChainDesc, textureSetPB) != ovrSuccess) { + throw new RuntimeException("Failed to create Swap Texture Set"); + } + chain = textureSetPB.get(0); + swapChainDesc.free(); + System.out.println("done chain creation"); + + return textureSetPB; + } + + private void setupLayers() { + PointerBuffer chainPtr = setupTextureChain(); + + //Layers + layer0 = OVRLayerEyeFov.calloc(); + layer0.Header().Type(ovrLayerType_EyeFov); + layer0.Header().Flags(ovrLayerFlag_TextureOriginAtBottomLeft); + + for (int eye = 0; eye < 2; eye++) { + OVRRecti viewport = OVRRecti.calloc(); + viewport.Pos().x(0); + viewport.Pos().y(0); + viewport.Size().w(textureW); + viewport.Size().h(textureH); + + layer0.ColorTexture(chainPtr); + layer0.Viewport(eye, viewport); + layer0.Fov(eye, fovPorts[eye]); + + viewport.free(); + // we update pose only when we have it in the render loop + } + + layers = createPointerBuffer(1); + layers.put(0, layer0); + } + + /** + * Create framebuffers bound to each of the eye textures + */ + private void setupFramebuffers() { + // Find the chain length + IntBuffer length = BufferUtils.createIntBuffer(1); + ovr_GetTextureSwapChainLength(session, chain, length); + int chainLength = length.get(); + + System.out.println("chain length=" + chainLength); + + // Create the frame buffers + framebuffers = new FrameBuffer[chainLength]; + for (int i = 0; i < chainLength; i++) { + // find the GL texture ID for this texture + IntBuffer textureIdB = BufferUtils.createIntBuffer(1); + OVRGL.ovr_GetTextureSwapChainBufferGL(session, chain, i, textureIdB); + int textureId = textureIdB.get(); + + // TODO less hacky way of getting our texture into JMonkeyEngine + Texture2D tex = new Texture2D(new Image()); + tex.getImage().setId(textureId); + + FrameBuffer buffer = new FrameBuffer(textureW, textureH, 1); + buffer.setDepthBuffer(Image.Format.Depth); + buffer.setColorTexture(tex); + + framebuffers[i] = buffer; + } + } + + @Override + public void update(float tpf) { + // TODO + } + + @Override + public void render() { + for (int eye = 0; eye < 2; eye++) { + // TODO add eyePoses +// OVRPosef eyePose = eyePoses[eye]; +// layer0.RenderPose(eye, eyePose); + + IntBuffer currentIndexB = BufferUtils.createIntBuffer(1); + ovr_GetTextureSwapChainCurrentIndex(session, chain, currentIndexB); + int index = currentIndexB.get(); + + (eye == 0 ? leftViewPort : rightViewPort).setOutputFrameBuffer(framebuffers[index]); + } + + // Now the game will render into the buffers given to us by LibOVR + } + + @Override + public void postRender() { + // We're done with our textures now - the game is done drawing into them. + ovr_CommitTextureSwapChain(session, chain); + + // Send the result to the HMD + int result = ovr_SubmitFrame(session, 0, null, layers); + if (result != ovrSuccess) { + throw new IllegalStateException("Failed to submit frame!"); + } + } + + /* + ********************************************************* + * Show's over, now it's just boring camera stuff etc. * + ********************************************************* + */ + /** + * Set up the cameras and views for each eye and the mirror display. + */ + private void setupCamerasAndViews() { + // TODO: Use LobOVR IPD etc + if (environment != null) { + // get desired frustrum from original camera + Camera origCam = environment.getCamera(); + float fFar = origCam.getFrustumFar(); + float fNear = origCam.getFrustumNear(); + + // restore frustrum on distortion scene cam, if needed + if (environment.isInstanceRendering()) { + leftCamera = origCam; + } else if (environment.compositorAllowed() == false) { + origCam.setFrustumFar(100f); + origCam.setFrustumNear(1f); + leftCamera = origCam.clone(); + prepareCameraSize(origCam, 2f); + } else { + leftCamera = origCam.clone(); + } + + getLeftCamera().setFrustumPerspective(environment.getDefaultFOV(), environment.getDefaultAspect(), fNear, fFar); + + prepareCameraSize(getLeftCamera(), 1f); + if (environment.getVRHardware() != null) { + getLeftCamera().setProjectionMatrix(environment.getVRHardware().getHMDMatrixProjectionLeftEye(getLeftCamera())); + } + //org.lwjgl.opengl.GL11.glEnable(org.lwjgl.opengl.GL30.GL_FRAMEBUFFER_SRGB); + + if (!environment.isInstanceRendering()) { + leftViewPort = setupViewBuffers(getLeftCamera(), LEFT_VIEW_NAME); + rightCamera = getLeftCamera().clone(); + if (environment.getVRHardware() != null) { + getRightCamera().setProjectionMatrix(environment.getVRHardware().getHMDMatrixProjectionRightEye(getRightCamera())); + } + rightViewPort = setupViewBuffers(getRightCamera(), RIGHT_VIEW_NAME); + } else if (environment.getApplication() != null) { + + LOG.severe("THIS CODE NEED CHANGES !!!"); + leftViewPort = environment.getApplication().getViewPort(); + //leftViewport.attachScene(app.getRootNode()); + rightCamera = getLeftCamera().clone(); + if (environment.getVRHardware() != null) { + getRightCamera().setProjectionMatrix(environment.getVRHardware().getHMDMatrixProjectionRightEye(getRightCamera())); + } + + org.lwjgl.opengl.GL11.glEnable(org.lwjgl.opengl.GL30.GL_CLIP_DISTANCE0); + + //FIXME: [jme-vr] Fix with JMonkey next release + //RenderManager._VRInstancing_RightCamProjection = camRight.getViewProjectionMatrix(); + // TODO: Add LibOVR support + // setupFinalFullTexture(environment.getApplication().getViewPort().getCamera()); + } else { + throw new IllegalStateException("This VR environment is not attached to any application."); + } + + // setup gui + environment.getVRGUIManager().setupGui(getLeftCamera(), getRightCamera(), getLeftViewPort(), getRightViewPort()); + + if (environment.getVRHardware() != null) { + // call these to cache the results internally + environment.getVRHardware().getHMDMatrixPoseLeftEye(); + environment.getVRHardware().getHMDMatrixPoseRightEye(); + } + } else { + throw new IllegalStateException("This VR view manager is not attached to any VR environment."); + } + } + + private void prepareCameraSize(Camera cam, float xMult) { + // TODO this function is identical to that in VRViewManagerOpenVR; merge the two. + if (environment != null) { + if (environment.getApplication() != null) { + Vector2f size = new Vector2f(); + VRAPI vrhmd = environment.getVRHardware(); + + if (vrhmd == null) { + size.x = 1280f; + size.y = 720f; + } else { + vrhmd.getRenderSize(size); + } + + if (size.x < environment.getApplication().getContext().getSettings().getWidth()) { + size.x = environment.getApplication().getContext().getSettings().getWidth(); + } + if (size.y < environment.getApplication().getContext().getSettings().getHeight()) { + size.y = environment.getApplication().getContext().getSettings().getHeight(); + } + + if (environment.isInstanceRendering()) { + size.x *= 2f; + } + + // other adjustments + size.x *= xMult; + size.x *= getResolutionMuliplier(); + size.y *= getResolutionMuliplier(); + + if (cam.getWidth() != size.x || cam.getHeight() != size.y) { + cam.resize((int) size.x, (int) size.y, false); + } + } else { + throw new IllegalStateException("This VR environment is not attached to any application."); + } + } else { + throw new IllegalStateException("This VR view manager is not attached to any VR environment."); + } + } + + private ViewPort setupViewBuffers(Camera cam, String viewName) { + // TODO this function is identical to that in VRViewManagerOpenVR; merge the two. + if (environment != null) { + if (environment.getApplication() != null) { + // create offscreen framebuffer + FrameBuffer offBufferLeft = new FrameBuffer(cam.getWidth(), cam.getHeight(), 1); + //offBufferLeft.setSrgb(true); + + //setup framebuffer's texture + Texture2D offTex = new Texture2D(cam.getWidth(), cam.getHeight(), Image.Format.RGBA8); + offTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); + offTex.setMagFilter(Texture.MagFilter.Bilinear); + + //setup framebuffer to use texture + offBufferLeft.setDepthBuffer(Image.Format.Depth); + offBufferLeft.setColorTexture(offTex); + + ViewPort viewPort = environment.getApplication().getRenderManager().createPreView(viewName, cam); + viewPort.setClearFlags(true, true, true); + viewPort.setBackgroundColor(ColorRGBA.Black); + + Iterator spatialIter = environment.getApplication().getViewPort().getScenes().iterator(); + while (spatialIter.hasNext()) { + viewPort.attachScene(spatialIter.next()); + } + + //set viewport to render to offscreen framebuffer + viewPort.setOutputFrameBuffer(offBufferLeft); + return viewPort; + } else { + throw new IllegalStateException("This VR environment is not attached to any application."); + } + } else { + throw new IllegalStateException("This VR view manager is not attached to any VR environment."); + } + } +} From 7313abf58dc3638f7a985820eed89805e6b3ddd4 Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Thu, 28 Sep 2017 18:41:03 +1300 Subject: [PATCH 05/27] Setup infrastructure for using LibOVR --- jme3-vr/src/main/java/com/jme3/app/VRConstants.java | 7 +++++++ jme3-vr/src/main/java/com/jme3/app/VREnvironment.java | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/jme3-vr/src/main/java/com/jme3/app/VRConstants.java b/jme3-vr/src/main/java/com/jme3/app/VRConstants.java index 6e23ec2bf..d8ec7fea7 100644 --- a/jme3-vr/src/main/java/com/jme3/app/VRConstants.java +++ b/jme3-vr/src/main/java/com/jme3/app/VRConstants.java @@ -141,6 +141,13 @@ public class VRConstants { * @see #SETTING_VRAPI */ public static final int SETTING_VRAPI_OPENVR_LWJGL_VALUE = 3; + + /** + * The identifier of the LibOVR (Oculus) system. + * + * @see #SETTING_VRAPI + */ + public static final int SETTING_VRAPI_OCULUSVR_VALUE = 4; diff --git a/jme3-vr/src/main/java/com/jme3/app/VREnvironment.java b/jme3-vr/src/main/java/com/jme3/app/VREnvironment.java index e0e9048dc..7ab9e29ff 100644 --- a/jme3-vr/src/main/java/com/jme3/app/VREnvironment.java +++ b/jme3-vr/src/main/java/com/jme3/app/VREnvironment.java @@ -7,6 +7,7 @@ import java.util.logging.Logger; import com.jme3.app.state.AppState; import com.jme3.input.vr.OSVR; import com.jme3.input.vr.OpenVR; +import com.jme3.input.vr.OculusVR; import com.jme3.input.vr.VRAPI; import com.jme3.input.vr.VRBounds; import com.jme3.input.vr.VRInputAPI; @@ -18,6 +19,7 @@ import com.jme3.util.VRGuiManager; import com.jme3.util.VRMouseManager; import com.jme3.util.VRViewManager; import com.jme3.util.VRViewManagerOSVR; +import com.jme3.util.VRViewManagerOculus; import com.jme3.util.VRViewManagerOpenVR; public class VREnvironment { @@ -388,6 +390,8 @@ public class VREnvironment { viewmanager = new VRViewManagerOpenVR(this); } else if (vrBinding == VRConstants.SETTING_VRAPI_OSVR_VALUE){ viewmanager = new VRViewManagerOSVR(this); + } else if (vrBinding == VRConstants.SETTING_VRAPI_OCULUSVR_VALUE) { + viewmanager = new VRViewManagerOculus(this); } else { logger.severe("Cannot instanciate view manager, unknown VRAPI type: "+vrBinding); } @@ -419,6 +423,10 @@ public class VREnvironment { hardware = new OpenVR(this); initialized = true; logger.config("Creating OpenVR wrapper [SUCCESS]"); + } else if (vrBinding == VRConstants.SETTING_VRAPI_OCULUSVR_VALUE) { + hardware = new OculusVR(this); + initialized = true; + logger.config("Creating LibOVR wrapper [SUCCESS]"); } else { logger.config("Cannot create VR binding: "+vrBinding+" [FAILED]"); logger.log(Level.SEVERE, "Cannot initialize VR environment [FAILED]"); From 3ac7888dc03b803d6ab241e4b9219b8000e5b6be Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Thu, 28 Sep 2017 22:15:50 +1300 Subject: [PATCH 06/27] Add initialization logic to OculusVR input --- .../main/java/com/jme3/input/vr/OculusVR.java | 253 +++++++++++++++++- 1 file changed, 240 insertions(+), 13 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java index a26060379..7e178d65a 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java @@ -8,10 +8,24 @@ package com.jme3.input.vr; import com.jme3.app.VREnvironment; import com.jme3.math.*; 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 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 org.lwjgl.BufferUtils; +import org.lwjgl.PointerBuffer; + import org.lwjgl.ovr.*; /** @@ -26,6 +40,57 @@ public class OculusVR implements VRAPI { private final VREnvironment environment; 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) { this.environment = environment; } @@ -42,23 +107,117 @@ public class OculusVR implements VRAPI { @Override 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 public boolean initialize() { OVRDetectResult detect = OVRDetectResult.calloc(); ovr_Detect(0, detect); - System.out.println("OVRDetectResult.IsOculusHMDConnected = " + detect.IsOculusHMDConnected()); - System.out.println("OVRDetectResult.IsOculusServiceRunning = " + detect.IsOculusServiceRunning()); + boolean connected = detect.IsOculusHMDConnected(); + LOGGER.info("OVRDetectResult.IsOculusHMDConnected = " + connected); + LOGGER.info("OVRDetectResult.IsOculusServiceRunning = " + detect.IsOculusServiceRunning()); detect.free(); - if (detect.IsOculusHMDConnected() == false) { + + if (!connected) { return false; } 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 @@ -68,14 +227,18 @@ public class OculusVR implements VRAPI { @Override public void destroy() { + throw new UnsupportedOperationException(); } @Override public void reset() { + throw new UnsupportedOperationException(); } @Override public void getRenderSize(Vector2f store) { + store.x = resolutionW; + store.y = resolutionH; } @Override @@ -105,12 +268,12 @@ public class OculusVR implements VRAPI { @Override public Matrix4f getHMDMatrixProjectionLeftEye(Camera cam) { - throw new UnsupportedOperationException(); + return matrixO2J(projections[ovrEye_Left], new Matrix4f()); } @Override public Matrix4f getHMDMatrixProjectionRightEye(Camera cam) { - throw new UnsupportedOperationException(); + return matrixO2J(projections[ovrEye_Right], new Matrix4f()); } @Override @@ -130,19 +293,28 @@ public class OculusVR implements VRAPI { @Override public Matrix4f getHMDMatrixPoseLeftEye() { - throw new UnsupportedOperationException(); + return eyePoses[ovrEye_Left]; } @Override - public HmdType getType() { - throw new UnsupportedOperationException(); + public Matrix4f getHMDMatrixPoseRightEye() { + return eyePoses[ovrEye_Left]; } @Override - public Matrix4f getHMDMatrixPoseRightEye() { + public HmdType getType() { 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) { throw new UnsupportedOperationException("Not yet implemented!"); } @@ -159,7 +331,62 @@ public class OculusVR implements VRAPI { throw new UnsupportedOperationException("Not yet implemented!"); } - public boolean initVRCompositor(boolean set) { - throw new UnsupportedOperationException("Not yet implemented!"); + // UTILITIES + // 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: */ + From 0844c9da04514342adbb66ecfe728099ede5b65d Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Thu, 28 Sep 2017 22:37:09 +1300 Subject: [PATCH 07/27] Add getters to OculusVR input --- .../main/java/com/jme3/input/vr/OculusVR.java | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java index 7e178d65a..0dba2292b 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java @@ -8,25 +8,15 @@ package com.jme3.input.vr; import com.jme3.app.VREnvironment; import com.jme3.math.*; import com.jme3.renderer.Camera; -import com.jme3.util.VRUtil; +import org.lwjgl.PointerBuffer; +import org.lwjgl.ovr.*; -import java.nio.IntBuffer; -import java.util.Locale; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; 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.OVRErrorCode.ovrSuccess; import static org.lwjgl.ovr.OVRUtil.ovr_Detect; - -import org.lwjgl.BufferUtils; -import org.lwjgl.PointerBuffer; - -import org.lwjgl.ovr.*; +import static org.lwjgl.system.MemoryUtil.*; /** * Oculus VR (LibOVR 1.3.0) Native support. @@ -386,6 +376,19 @@ public class OculusVR implements VRAPI { return to; } + + // Getters, intended for VRViewManager. + public OVRHmdDesc getHmdDesc() { + return hmdDesc; + } + + public OVRFovPort[] getFovPorts() { + return fovPorts; + } + + public long getSessionPointer() { + return session; + } } /* vim: set ts=4 softtabstop=0 sw=4 expandtab: */ From f0b4c13515bea20689cc6bded27c52420d3bfb35 Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Thu, 28 Sep 2017 22:38:05 +1300 Subject: [PATCH 08/27] Use real, not stub, values in VRViewManagerOculus --- .../com/jme3/util/VRViewManagerOculus.java | 70 +++++++++---------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java index 4f61f4dd3..7c1fa5637 100644 --- a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java +++ b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java @@ -32,6 +32,7 @@ package com.jme3.util; import com.jme3.app.VREnvironment; +import com.jme3.input.vr.OculusVR; import com.jme3.input.vr.VRAPI; import com.jme3.math.ColorRGBA; import com.jme3.math.Vector2f; @@ -39,13 +40,12 @@ import com.jme3.renderer.Camera; import com.jme3.renderer.ViewPort; import com.jme3.scene.Spatial; import com.jme3.texture.*; + import java.nio.IntBuffer; import java.util.Iterator; import java.util.logging.Logger; -import org.lwjgl.PointerBuffer; -import static com.jme3.util.VRViewManager.LEFT_VIEW_NAME; -import static com.jme3.util.VRViewManager.RIGHT_VIEW_NAME; +import org.lwjgl.PointerBuffer; import org.lwjgl.ovr.*; @@ -63,13 +63,12 @@ public class VRViewManagerOculus extends AbstractVRViewManager { private static final Logger LOG = Logger.getLogger(VRViewManagerOculus.class.getName()); private final VREnvironment environment; + private final OculusVR hardware; // The size of the texture drawn onto the HMD private int textureW; private int textureH; - long session = 0; // TODO take from OculusVR input - // Layers to render into private PointerBuffer layers; private OVRLayerEyeFov layer0; @@ -77,12 +76,7 @@ public class VRViewManagerOculus extends AbstractVRViewManager { /** * Chain texture set thing. */ - long chain; - - /** - * The definitions of the up/down/left/right parts of the FOV for each eye. - */ - private final OVRFovPort fovPorts[] = new OVRFovPort[2]; + private long chain; /** * Frame buffers we can draw into. @@ -92,6 +86,13 @@ public class VRViewManagerOculus extends AbstractVRViewManager { public VRViewManagerOculus(VREnvironment environment) { this.environment = environment; + VRAPI hardware = environment.getVRHardware(); + if (!(hardware instanceof OculusVR)) { + throw new IllegalStateException("Cannot use Oculus VR view manager on non-Oculus hardware state!"); + } + + this.hardware = (OculusVR) hardware; + if (!environment.compositorAllowed()) { throw new UnsupportedOperationException("Cannot render without compositor on LibOVR"); } @@ -101,36 +102,23 @@ public class VRViewManagerOculus extends AbstractVRViewManager { public void initialize() { setupCamerasAndViews(); - OVRHmdDesc hmdDesc = null; // TODO take from OculusVR input - - 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() - ); - } - findHMDTextureSize(); - setupTextureChain(); setupLayers(); setupFramebuffers(); } private void findHMDTextureSize() { - OVRFovPort fovPorts[] = null; + OVRFovPort fovPorts[] = hardware.getFovPorts(); // Texture sizes float pixelScaling = 1.0f; // pixelsPerDisplayPixel OVRSizei leftTextureSize = OVRSizei.malloc(); - ovr_GetFovTextureSize(session, ovrEye_Left, fovPorts[ovrEye_Left], pixelScaling, leftTextureSize); + ovr_GetFovTextureSize(session(), ovrEye_Left, fovPorts[ovrEye_Left], pixelScaling, leftTextureSize); System.out.println("leftTextureSize W=" + leftTextureSize.w() + ", H=" + leftTextureSize.h()); OVRSizei rightTextureSize = OVRSizei.malloc(); - ovr_GetFovTextureSize(session, ovrEye_Right, fovPorts[ovrEye_Right], pixelScaling, rightTextureSize); + ovr_GetFovTextureSize(session(), ovrEye_Right, fovPorts[ovrEye_Right], pixelScaling, rightTextureSize); System.out.println("rightTextureSize W=" + rightTextureSize.w() + ", H=" + rightTextureSize.h()); textureW = leftTextureSize.w() + rightTextureSize.w(); @@ -140,6 +128,10 @@ public class VRViewManagerOculus extends AbstractVRViewManager { rightTextureSize.free(); } + private long session() { + return hardware.getSessionPointer(); + } + private PointerBuffer setupTextureChain() { // Set up the information for the texture buffer chain thing OVRTextureSwapChainDesc swapChainDesc = OVRTextureSwapChainDesc.calloc() @@ -154,7 +146,7 @@ public class VRViewManagerOculus extends AbstractVRViewManager { // Create the chain PointerBuffer textureSetPB = createPointerBuffer(1); - if (OVRGL.ovr_CreateTextureSwapChainGL(session, swapChainDesc, textureSetPB) != ovrSuccess) { + if (OVRGL.ovr_CreateTextureSwapChainGL(session(), swapChainDesc, textureSetPB) != ovrSuccess) { throw new RuntimeException("Failed to create Swap Texture Set"); } chain = textureSetPB.get(0); @@ -181,7 +173,7 @@ public class VRViewManagerOculus extends AbstractVRViewManager { layer0.ColorTexture(chainPtr); layer0.Viewport(eye, viewport); - layer0.Fov(eye, fovPorts[eye]); + layer0.Fov(eye, hardware.getFovPorts()[eye]); viewport.free(); // we update pose only when we have it in the render loop @@ -197,7 +189,7 @@ public class VRViewManagerOculus extends AbstractVRViewManager { private void setupFramebuffers() { // Find the chain length IntBuffer length = BufferUtils.createIntBuffer(1); - ovr_GetTextureSwapChainLength(session, chain, length); + ovr_GetTextureSwapChainLength(session(), chain, length); int chainLength = length.get(); System.out.println("chain length=" + chainLength); @@ -207,12 +199,17 @@ public class VRViewManagerOculus extends AbstractVRViewManager { for (int i = 0; i < chainLength; i++) { // find the GL texture ID for this texture IntBuffer textureIdB = BufferUtils.createIntBuffer(1); - OVRGL.ovr_GetTextureSwapChainBufferGL(session, chain, i, textureIdB); + OVRGL.ovr_GetTextureSwapChainBufferGL(session(), chain, i, textureIdB); int textureId = textureIdB.get(); // TODO less hacky way of getting our texture into JMonkeyEngine - Texture2D tex = new Texture2D(new Image()); - tex.getImage().setId(textureId); + Image img = new Image(); + img.setId(textureId); + img.setFormat(Image.Format.RGBA8); + img.setWidth(textureW); + img.setHeight(textureH); + + Texture2D tex = new Texture2D(img); FrameBuffer buffer = new FrameBuffer(textureW, textureH, 1); buffer.setDepthBuffer(Image.Format.Depth); @@ -235,7 +232,7 @@ public class VRViewManagerOculus extends AbstractVRViewManager { // layer0.RenderPose(eye, eyePose); IntBuffer currentIndexB = BufferUtils.createIntBuffer(1); - ovr_GetTextureSwapChainCurrentIndex(session, chain, currentIndexB); + ovr_GetTextureSwapChainCurrentIndex(session(), chain, currentIndexB); int index = currentIndexB.get(); (eye == 0 ? leftViewPort : rightViewPort).setOutputFrameBuffer(framebuffers[index]); @@ -247,10 +244,10 @@ public class VRViewManagerOculus extends AbstractVRViewManager { @Override public void postRender() { // We're done with our textures now - the game is done drawing into them. - ovr_CommitTextureSwapChain(session, chain); + ovr_CommitTextureSwapChain(session(), chain); // Send the result to the HMD - int result = ovr_SubmitFrame(session, 0, null, layers); + int result = ovr_SubmitFrame(session(), 0, null, layers); if (result != ovrSuccess) { throw new IllegalStateException("Failed to submit frame!"); } @@ -261,6 +258,7 @@ public class VRViewManagerOculus extends AbstractVRViewManager { * Show's over, now it's just boring camera stuff etc. * ********************************************************* */ + /** * Set up the cameras and views for each eye and the mirror display. */ From 1c975918b1c441816ad357ae236fe6c9169b4e34 Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Thu, 28 Sep 2017 23:40:04 +1300 Subject: [PATCH 09/27] OculusVR: Call GetPredictedDisplayTime to satasfy SubmitFrame --- .../src/main/java/com/jme3/input/vr/OculusVR.java | 14 +++++++++----- .../java/com/jme3/util/VRViewManagerOculus.java | 2 ++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java index 0dba2292b..75cf16f4d 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java @@ -210,6 +210,15 @@ public class OculusVR implements VRAPI { return true; } + @Override + public void updatePose() { + double ftiming = ovr_GetPredictedDisplayTime(session, 0); + OVRTrackingState hmdState = OVRTrackingState.malloc(); + ovr_GetTrackingState(session, ftiming, true, hmdState); + + // TODO + } + @Override public boolean isInitialized() { return initialized; @@ -251,11 +260,6 @@ public class OculusVR implements VRAPI { throw new UnsupportedOperationException(); } - @Override - public void updatePose() { - throw new UnsupportedOperationException(); - } - @Override public Matrix4f getHMDMatrixProjectionLeftEye(Camera cam) { return matrixO2J(projections[ovrEye_Left], new Matrix4f()); diff --git a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java index 7c1fa5637..268d5a290 100644 --- a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java +++ b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java @@ -222,6 +222,8 @@ public class VRViewManagerOculus extends AbstractVRViewManager { @Override public void update(float tpf) { // TODO + + hardware.updatePose(); } @Override From 8a3336704a9f8c156ebca6c3c0544830c97c6fc2 Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Fri, 29 Sep 2017 00:10:05 +1300 Subject: [PATCH 10/27] Move rendering setup from Oculus VRViewManager to OculusVR, and implement cleanup --- .../main/java/com/jme3/input/vr/OculusVR.java | 180 +++++++++++++++++- .../com/jme3/util/VRViewManagerOculus.java | 138 +------------- 2 files changed, 177 insertions(+), 141 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java index 75cf16f4d..c10a23d89 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java @@ -8,11 +8,17 @@ package com.jme3.input.vr; import com.jme3.app.VREnvironment; import com.jme3.math.*; import com.jme3.renderer.Camera; +import com.jme3.texture.FrameBuffer; +import com.jme3.texture.Image; +import com.jme3.texture.Texture2D; +import org.lwjgl.BufferUtils; import org.lwjgl.PointerBuffer; import org.lwjgl.ovr.*; +import java.nio.IntBuffer; import java.util.logging.Logger; +import static org.lwjgl.BufferUtils.createPointerBuffer; import static org.lwjgl.ovr.OVR.*; import static org.lwjgl.ovr.OVRErrorCode.ovrSuccess; import static org.lwjgl.ovr.OVRUtil.ovr_Detect; @@ -81,6 +87,24 @@ public class OculusVR implements VRAPI { */ private final Matrix4f[] eyePoses = new Matrix4f[2]; + // The size of the texture drawn onto the HMD + private int textureW; + private int textureH; + + // Layers to render into + private PointerBuffer layers; + private OVRLayerEyeFov layer0; + + /** + * Chain texture set thing. + */ + private long chain; + + /** + * Frame buffers we can draw into. + */ + private FrameBuffer framebuffers[]; + public OculusVR(VREnvironment environment) { this.environment = environment; } @@ -226,7 +250,32 @@ public class OculusVR implements VRAPI { @Override public void destroy() { - throw new UnsupportedOperationException(); + // fovPorts: contents are managed by LibOVR, no need to do anything. + + // Check if we've set up rendering - if so, clean that up. + if (chain != 0) { + // Destroy our set of huge buffer images. + ovr_DestroyTextureSwapChain(session, chain); + + // Free up the layer + layer0.free(); + + // The layers array apparently takes care of itself (and crashes if we try to free it) + } + + for (OVREyeRenderDesc eye : eyeRenderDesc) { + eye.free(); + } + for (OVRMatrix4f projection : projections) { + projection.free(); + } + + hmdDesc.free(); + sessionStatus.free(); + + // Wrap everything up + ovr_Destroy(session); + ovr_Shutdown(); } @Override @@ -305,6 +354,10 @@ public class OculusVR implements VRAPI { throw new UnsupportedOperationException("Cannot use LibOVR without compositor!"); } + findHMDTextureSize(); + setupLayers(); + setupFramebuffers(); + // TODO move initialization code here from VRViewManagerOculus return true; } @@ -325,6 +378,114 @@ public class OculusVR implements VRAPI { throw new UnsupportedOperationException("Not yet implemented!"); } + // Rendering-type stuff + + public void findHMDTextureSize() { + // Texture sizes + float pixelScaling = 1.0f; // pixelsPerDisplayPixel + + OVRSizei leftTextureSize = OVRSizei.malloc(); + ovr_GetFovTextureSize(session, ovrEye_Left, fovPorts[ovrEye_Left], pixelScaling, leftTextureSize); + System.out.println("leftTextureSize W=" + leftTextureSize.w() + ", H=" + leftTextureSize.h()); + + OVRSizei rightTextureSize = OVRSizei.malloc(); + ovr_GetFovTextureSize(session, ovrEye_Right, fovPorts[ovrEye_Right], pixelScaling, rightTextureSize); + System.out.println("rightTextureSize W=" + rightTextureSize.w() + ", H=" + rightTextureSize.h()); + + textureW = leftTextureSize.w() + rightTextureSize.w(); + textureH = Math.max(leftTextureSize.h(), rightTextureSize.h()); + + leftTextureSize.free(); + rightTextureSize.free(); + } + + private PointerBuffer setupTextureChain() { + // Set up the information for the texture buffer chain thing + OVRTextureSwapChainDesc swapChainDesc = OVRTextureSwapChainDesc.calloc() + .Type(ovrTexture_2D) + .ArraySize(1) + .Format(OVR_FORMAT_R8G8B8A8_UNORM_SRGB) + .Width(textureW) + .Height(textureH) + .MipLevels(1) + .SampleCount(1) + .StaticImage(false); // ovrFalse + + // Create the chain + PointerBuffer textureSetPB = createPointerBuffer(1); + if (OVRGL.ovr_CreateTextureSwapChainGL(session, swapChainDesc, textureSetPB) != ovrSuccess) { + throw new RuntimeException("Failed to create Swap Texture Set"); + } + chain = textureSetPB.get(0); + swapChainDesc.free(); + System.out.println("done chain creation"); + + return textureSetPB; + } + + public void setupLayers() { + PointerBuffer chainPtr = setupTextureChain(); + + //Layers + layer0 = OVRLayerEyeFov.calloc(); + layer0.Header().Type(ovrLayerType_EyeFov); + layer0.Header().Flags(ovrLayerFlag_TextureOriginAtBottomLeft); + + for (int eye = 0; eye < 2; eye++) { + OVRRecti viewport = OVRRecti.calloc(); + viewport.Pos().x(0); + viewport.Pos().y(0); + viewport.Size().w(textureW); + viewport.Size().h(textureH); + + layer0.ColorTexture(chainPtr); + layer0.Viewport(eye, viewport); + layer0.Fov(eye, fovPorts[eye]); + + viewport.free(); + // we update pose only when we have it in the render loop + } + + layers = createPointerBuffer(1); + layers.put(0, layer0); + } + + /** + * Create framebuffers bound to each of the eye textures + */ + public void setupFramebuffers() { + // Find the chain length + IntBuffer length = BufferUtils.createIntBuffer(1); + ovr_GetTextureSwapChainLength(session, chain, length); + int chainLength = length.get(); + + System.out.println("chain length=" + chainLength); + + // Create the frame buffers + framebuffers = new FrameBuffer[chainLength]; + for (int i = 0; i < chainLength; i++) { + // find the GL texture ID for this texture + IntBuffer textureIdB = BufferUtils.createIntBuffer(1); + OVRGL.ovr_GetTextureSwapChainBufferGL(session, chain, i, textureIdB); + int textureId = textureIdB.get(); + + // TODO less hacky way of getting our texture into JMonkeyEngine + Image img = new Image(); + img.setId(textureId); + img.setFormat(Image.Format.RGBA8); + img.setWidth(textureW); + img.setHeight(textureH); + + Texture2D tex = new Texture2D(img); + + FrameBuffer buffer = new FrameBuffer(textureW, textureH, 1); + buffer.setDepthBuffer(Image.Format.Depth); + buffer.setColorTexture(tex); + + framebuffers[i] = buffer; + } + } + // UTILITIES // TODO move to helper class @@ -382,16 +543,21 @@ public class OculusVR implements VRAPI { } // Getters, intended for VRViewManager. - public OVRHmdDesc getHmdDesc() { - return hmdDesc; + + public long getSessionPointer() { + return session; } - public OVRFovPort[] getFovPorts() { - return fovPorts; + public long getChain() { + return chain; } - public long getSessionPointer() { - return session; + public FrameBuffer[] getFramebuffers() { + return framebuffers; + } + + public PointerBuffer getLayers() { + return layers; } } diff --git a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java index 268d5a290..fb442be32 100644 --- a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java +++ b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java @@ -65,24 +65,6 @@ public class VRViewManagerOculus extends AbstractVRViewManager { private final VREnvironment environment; private final OculusVR hardware; - // The size of the texture drawn onto the HMD - private int textureW; - private int textureH; - - // Layers to render into - private PointerBuffer layers; - private OVRLayerEyeFov layer0; - - /** - * Chain texture set thing. - */ - private long chain; - - /** - * Frame buffers we can draw into. - */ - private FrameBuffer framebuffers[]; - public VRViewManagerOculus(VREnvironment environment) { this.environment = environment; @@ -101,124 +83,12 @@ public class VRViewManagerOculus extends AbstractVRViewManager { @Override public void initialize() { setupCamerasAndViews(); - - findHMDTextureSize(); - setupLayers(); - setupFramebuffers(); - } - - private void findHMDTextureSize() { - OVRFovPort fovPorts[] = hardware.getFovPorts(); - - // Texture sizes - float pixelScaling = 1.0f; // pixelsPerDisplayPixel - - OVRSizei leftTextureSize = OVRSizei.malloc(); - ovr_GetFovTextureSize(session(), ovrEye_Left, fovPorts[ovrEye_Left], pixelScaling, leftTextureSize); - System.out.println("leftTextureSize W=" + leftTextureSize.w() + ", H=" + leftTextureSize.h()); - - OVRSizei rightTextureSize = OVRSizei.malloc(); - ovr_GetFovTextureSize(session(), ovrEye_Right, fovPorts[ovrEye_Right], pixelScaling, rightTextureSize); - System.out.println("rightTextureSize W=" + rightTextureSize.w() + ", H=" + rightTextureSize.h()); - - textureW = leftTextureSize.w() + rightTextureSize.w(); - textureH = Math.max(leftTextureSize.h(), rightTextureSize.h()); - - leftTextureSize.free(); - rightTextureSize.free(); } private long session() { return hardware.getSessionPointer(); } - private PointerBuffer setupTextureChain() { - // Set up the information for the texture buffer chain thing - OVRTextureSwapChainDesc swapChainDesc = OVRTextureSwapChainDesc.calloc() - .Type(ovrTexture_2D) - .ArraySize(1) - .Format(OVR_FORMAT_R8G8B8A8_UNORM_SRGB) - .Width(textureW) - .Height(textureH) - .MipLevels(1) - .SampleCount(1) - .StaticImage(false); // ovrFalse - - // Create the chain - PointerBuffer textureSetPB = createPointerBuffer(1); - if (OVRGL.ovr_CreateTextureSwapChainGL(session(), swapChainDesc, textureSetPB) != ovrSuccess) { - throw new RuntimeException("Failed to create Swap Texture Set"); - } - chain = textureSetPB.get(0); - swapChainDesc.free(); - System.out.println("done chain creation"); - - return textureSetPB; - } - - private void setupLayers() { - PointerBuffer chainPtr = setupTextureChain(); - - //Layers - layer0 = OVRLayerEyeFov.calloc(); - layer0.Header().Type(ovrLayerType_EyeFov); - layer0.Header().Flags(ovrLayerFlag_TextureOriginAtBottomLeft); - - for (int eye = 0; eye < 2; eye++) { - OVRRecti viewport = OVRRecti.calloc(); - viewport.Pos().x(0); - viewport.Pos().y(0); - viewport.Size().w(textureW); - viewport.Size().h(textureH); - - layer0.ColorTexture(chainPtr); - layer0.Viewport(eye, viewport); - layer0.Fov(eye, hardware.getFovPorts()[eye]); - - viewport.free(); - // we update pose only when we have it in the render loop - } - - layers = createPointerBuffer(1); - layers.put(0, layer0); - } - - /** - * Create framebuffers bound to each of the eye textures - */ - private void setupFramebuffers() { - // Find the chain length - IntBuffer length = BufferUtils.createIntBuffer(1); - ovr_GetTextureSwapChainLength(session(), chain, length); - int chainLength = length.get(); - - System.out.println("chain length=" + chainLength); - - // Create the frame buffers - framebuffers = new FrameBuffer[chainLength]; - for (int i = 0; i < chainLength; i++) { - // find the GL texture ID for this texture - IntBuffer textureIdB = BufferUtils.createIntBuffer(1); - OVRGL.ovr_GetTextureSwapChainBufferGL(session(), chain, i, textureIdB); - int textureId = textureIdB.get(); - - // TODO less hacky way of getting our texture into JMonkeyEngine - Image img = new Image(); - img.setId(textureId); - img.setFormat(Image.Format.RGBA8); - img.setWidth(textureW); - img.setHeight(textureH); - - Texture2D tex = new Texture2D(img); - - FrameBuffer buffer = new FrameBuffer(textureW, textureH, 1); - buffer.setDepthBuffer(Image.Format.Depth); - buffer.setColorTexture(tex); - - framebuffers[i] = buffer; - } - } - @Override public void update(float tpf) { // TODO @@ -234,10 +104,10 @@ public class VRViewManagerOculus extends AbstractVRViewManager { // layer0.RenderPose(eye, eyePose); IntBuffer currentIndexB = BufferUtils.createIntBuffer(1); - ovr_GetTextureSwapChainCurrentIndex(session(), chain, currentIndexB); + ovr_GetTextureSwapChainCurrentIndex(session(), hardware.getChain(), currentIndexB); int index = currentIndexB.get(); - (eye == 0 ? leftViewPort : rightViewPort).setOutputFrameBuffer(framebuffers[index]); + (eye == 0 ? leftViewPort : rightViewPort).setOutputFrameBuffer(hardware.getFramebuffers()[index]); } // Now the game will render into the buffers given to us by LibOVR @@ -246,10 +116,10 @@ public class VRViewManagerOculus extends AbstractVRViewManager { @Override public void postRender() { // We're done with our textures now - the game is done drawing into them. - ovr_CommitTextureSwapChain(session(), chain); + ovr_CommitTextureSwapChain(session(), hardware.getChain()); // Send the result to the HMD - int result = ovr_SubmitFrame(session(), 0, null, layers); + int result = ovr_SubmitFrame(session(), 0, null, hardware.getLayers()); if (result != ovrSuccess) { throw new IllegalStateException("Failed to submit frame!"); } From 601ba1cfda0314e44862da389ce86e83cc2ecbaa Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Fri, 29 Sep 2017 09:13:57 +1300 Subject: [PATCH 11/27] OculusVR: Add basic camera positioning --- .../main/java/com/jme3/app/VRAppState.java | 8 +- .../main/java/com/jme3/input/vr/OculusVR.java | 29 +++++++ .../com/jme3/util/VRViewManagerOculus.java | 83 +++++++++++++++++-- 3 files changed, 109 insertions(+), 11 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/app/VRAppState.java b/jme3-vr/src/main/java/com/jme3/app/VRAppState.java index 39df3884b..806f2cc42 100644 --- a/jme3-vr/src/main/java/com/jme3/app/VRAppState.java +++ b/jme3-vr/src/main/java/com/jme3/app/VRAppState.java @@ -399,14 +399,14 @@ public class VRAppState extends AbstractAppState { //FIXME: check if this code is necessary. // Updates scene and gui states. - Iterator spatialIter = application.getViewPort().getScenes().iterator(); + Iterator spatialIter = getLeftViewPort().getScenes().iterator(); Spatial spatial = null; while(spatialIter.hasNext()){ spatial = spatialIter.next(); spatial.updateLogicalState(tpf); spatial.updateGeometricState(); - } - + } + if( environment.isInVR() == false || environment.getVRGUIManager().getPositioningMode() == VRGUIPositioningMode.MANUAL ) { // only update geometric state here if GUI is in manual mode, or not in VR // it will get updated automatically in the viewmanager update otherwise @@ -419,7 +419,7 @@ public class VRAppState extends AbstractAppState { } // use the analog control on the first tracked controller to push around the mouse - environment.getVRMouseManager().updateAnalogAsMouse(0, null, null, null, tpf); + // environment.getVRMouseManager().updateAnalogAsMouse(0, null, null, null, tpf); } @Override diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java index c10a23d89..3ba52e481 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java @@ -87,6 +87,11 @@ public class OculusVR implements VRAPI { */ private final Matrix4f[] eyePoses = new Matrix4f[2]; + /** + * The eye poses, as used during rendering. + */ + private final OVRPosef eyePosesPtr[] = new OVRPosef[2]; + // The size of the texture drawn onto the HMD private int textureW; private int textureH; @@ -240,6 +245,22 @@ public class OculusVR implements VRAPI { OVRTrackingState hmdState = OVRTrackingState.malloc(); ovr_GetTrackingState(session, ftiming, true, hmdState); + //get head pose + OVRPosef headPose = hmdState.HeadPose().ThePose(); + hmdState.free(); + + //build view offsets struct + OVRPosef.Buffer hmdToEyeOffsets = OVRPosef.calloc(2); + hmdToEyeOffsets.put(0, eyeRenderDesc[ovrEye_Left].HmdToEyePose()); + hmdToEyeOffsets.put(1, eyeRenderDesc[ovrEye_Right].HmdToEyePose()); + + //calculate eye poses + OVRPosef.Buffer outEyePoses = OVRPosef.create(2); + OVRUtil.ovr_CalcEyePoses(headPose, hmdToEyeOffsets, outEyePoses); + hmdToEyeOffsets.free(); + eyePosesPtr[ovrEye_Left] = outEyePoses.get(0); + eyePosesPtr[ovrEye_Right] = outEyePoses.get(1); + // TODO } @@ -559,6 +580,14 @@ public class OculusVR implements VRAPI { public PointerBuffer getLayers() { return layers; } + + public OVRLayerEyeFov getLayer0() { + return layer0; + } + + public OVRPosef[] getEyePosesPtr() { + return eyePosesPtr; + } } /* vim: set ts=4 softtabstop=0 sw=4 expandtab: */ diff --git a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java index fb442be32..64fffa988 100644 --- a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java +++ b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java @@ -35,7 +35,9 @@ import com.jme3.app.VREnvironment; import com.jme3.input.vr.OculusVR; import com.jme3.input.vr.VRAPI; import com.jme3.math.ColorRGBA; +import com.jme3.math.Quaternion; import com.jme3.math.Vector2f; +import com.jme3.math.Vector3f; import com.jme3.renderer.Camera; import com.jme3.renderer.ViewPort; import com.jme3.scene.Spatial; @@ -45,11 +47,8 @@ import java.nio.IntBuffer; import java.util.Iterator; import java.util.logging.Logger; -import org.lwjgl.PointerBuffer; - import org.lwjgl.ovr.*; -import static org.lwjgl.BufferUtils.*; import static org.lwjgl.ovr.OVR.*; import static org.lwjgl.ovr.OVRErrorCode.*; @@ -65,6 +64,13 @@ public class VRViewManagerOculus extends AbstractVRViewManager { private final VREnvironment environment; private final OculusVR hardware; + // Copied from OSVR + //final & temp values for camera calculations + private final Vector3f finalPosition = new Vector3f(); + private final Quaternion finalRotation = new Quaternion(); + private final Vector3f hmdPos = new Vector3f(); + private final Quaternion hmdRot = new Quaternion(); + public VRViewManagerOculus(VREnvironment environment) { this.environment = environment; @@ -94,20 +100,83 @@ public class VRViewManagerOculus extends AbstractVRViewManager { // TODO hardware.updatePose(); + + // TODO deduplicate + if (environment != null) { + // grab the observer + Object obs = environment.getObserver(); + Quaternion objRot; + Vector3f objPos; + if (obs instanceof Camera) { + objRot = ((Camera) obs).getRotation(); + objPos = ((Camera) obs).getLocation(); + } else { + objRot = ((Spatial) obs).getWorldRotation(); + objPos = ((Spatial) obs).getWorldTranslation(); + } + // grab the hardware handle + VRAPI dev = environment.getVRHardware(); + if (dev != null) { + // update the HMD's position & orientation + dev.getPositionAndOrientation(hmdPos, hmdRot); + if (obs != null) { + // update hmdPos based on obs rotation + finalRotation.set(objRot); + finalRotation.mult(hmdPos, hmdPos); + finalRotation.multLocal(hmdRot); + } + + finalizeCamera(dev.getHMDVectorPoseLeftEye(), objPos, leftCamera); + finalizeCamera(dev.getHMDVectorPoseRightEye(), objPos, rightCamera); + } else { + leftCamera.setFrame(objPos, objRot); + rightCamera.setFrame(objPos, objRot); + } + + if (environment.hasTraditionalGUIOverlay()) { + // update the mouse? + environment.getVRMouseManager().update(tpf); + + // update GUI position? + if (environment.getVRGUIManager().wantsReposition || environment.getVRGUIManager().getPositioningMode() != VRGUIPositioningMode.MANUAL) { + environment.getVRGUIManager().positionGuiNow(tpf); + environment.getVRGUIManager().updateGuiQuadGeometricState(); + } + } + } else { + throw new IllegalStateException("This VR view manager is not attached to any VR environment."); + } + } + + /** + * Place the camera within the scene. + * + * @param eyePos the eye position. + * @param obsPosition the observer position. + * @param cam the camera to place. + */ + private void finalizeCamera(Vector3f eyePos, Vector3f obsPosition, Camera cam) { + finalRotation.mult(eyePos, finalPosition); + finalPosition.addLocal(hmdPos); + if (obsPosition != null) { + finalPosition.addLocal(obsPosition); + } + finalPosition.y += getHeightAdjustment(); + cam.setFrame(finalPosition, finalRotation); } @Override public void render() { for (int eye = 0; eye < 2; eye++) { - // TODO add eyePoses -// OVRPosef eyePose = eyePoses[eye]; -// layer0.RenderPose(eye, eyePose); + // TODO do we need this? Don't we set the camera positions ourselves? + OVRPosef eyePose = hardware.getEyePosesPtr()[eye]; + hardware.getLayer0().RenderPose(eye, eyePose); IntBuffer currentIndexB = BufferUtils.createIntBuffer(1); ovr_GetTextureSwapChainCurrentIndex(session(), hardware.getChain(), currentIndexB); int index = currentIndexB.get(); - (eye == 0 ? leftViewPort : rightViewPort).setOutputFrameBuffer(hardware.getFramebuffers()[index]); + (eye == ovrEye_Left ? leftViewPort : rightViewPort).setOutputFrameBuffer(hardware.getFramebuffers()[index]); } // Now the game will render into the buffers given to us by LibOVR From 3082e63cfdd5ac33ba5638621e811af652bcc12c Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Fri, 29 Sep 2017 10:23:44 +1300 Subject: [PATCH 12/27] OculusVR: Implement further tracking methods --- .../main/java/com/jme3/input/vr/OculusVR.java | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java index 3ba52e481..ebb080bb6 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java @@ -92,6 +92,16 @@ public class OculusVR implements VRAPI { */ private final OVRPosef eyePosesPtr[] = new OVRPosef[2]; + /** + * The eye positions relative to the world, as used by jME. + */ + private final Vector3f eyePositions[] = new Vector3f[2]; + + /** + * The position and orientation of the user's head. + */ + private OVRPosef headPose; + // The size of the texture drawn onto the HMD private int textureW; private int textureH; @@ -229,6 +239,7 @@ public class OculusVR implements VRAPI { jPose.setRotationQuaternion(quatO2J(pose.Orientation(), new Quaternion())); eyePoses[eye] = jPose; + eyePositions[eye] = new Vector3f(); // Set the absolute position up for later. } // step 7 - recenter @@ -246,7 +257,7 @@ public class OculusVR implements VRAPI { ovr_GetTrackingState(session, ftiming, true, hmdState); //get head pose - OVRPosef headPose = hmdState.HeadPose().ThePose(); + headPose = hmdState.HeadPose().ThePose(); hmdState.free(); //build view offsets struct @@ -261,7 +272,9 @@ public class OculusVR implements VRAPI { eyePosesPtr[ovrEye_Left] = outEyePoses.get(0); eyePosesPtr[ovrEye_Right] = outEyePoses.get(1); - // TODO + for (int i = 0; i < eyePosesPtr.length; i++) { + vecO2J(eyePosesPtr[i].Position(), eyePositions[i]); + } } @Override @@ -317,17 +330,18 @@ public class OculusVR implements VRAPI { @Override public Quaternion getOrientation() { - throw new UnsupportedOperationException(); + return quatO2J(headPose.Orientation(), new Quaternion()); } @Override public Vector3f getPosition() { - throw new UnsupportedOperationException(); + return vecO2J(headPose.Position(), new Vector3f()); } @Override public void getPositionAndOrientation(Vector3f storePos, Quaternion storeRot) { - throw new UnsupportedOperationException(); + vecO2J(headPose.Position(), storePos); + quatO2J(headPose.Orientation(), storeRot); } @Override @@ -342,12 +356,12 @@ public class OculusVR implements VRAPI { @Override public Vector3f getHMDVectorPoseLeftEye() { - throw new UnsupportedOperationException(); + return eyePositions[ovrEye_Left]; } @Override public Vector3f getHMDVectorPoseRightEye() { - throw new UnsupportedOperationException(); + return eyePositions[ovrEye_Right]; } @Override From 563c3eabffd0bb67d7261175dc31fb3f0a6983cb Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Fri, 29 Sep 2017 10:24:49 +1300 Subject: [PATCH 13/27] OculusVR: Implement getType and rename eyePoses to hmdRelativeEyePoses --- .../src/main/java/com/jme3/input/vr/OculusVR.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java index ebb080bb6..65831d6f6 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java @@ -81,14 +81,14 @@ public class OculusVR implements VRAPI { private final OVRMatrix4f[] projections = new OVRMatrix4f[2]; /** - * Store the poses for each eye. + * Store the poses for each eye, relative to the HMD. * * @see #getHMDMatrixPoseLeftEye() */ - private final Matrix4f[] eyePoses = new Matrix4f[2]; + private final Matrix4f[] hmdRelativeEyePoses = new Matrix4f[2]; /** - * The eye poses, as used during rendering. + * The eye poses relative to the world, as used during rendering. */ private final OVRPosef eyePosesPtr[] = new OVRPosef[2]; @@ -238,7 +238,7 @@ public class OculusVR implements VRAPI { jPose.setTranslation(vecO2J(pose.Position(), new Vector3f())); jPose.setRotationQuaternion(quatO2J(pose.Orientation(), new Quaternion())); - eyePoses[eye] = jPose; + hmdRelativeEyePoses[eye] = jPose; eyePositions[eye] = new Vector3f(); // Set the absolute position up for later. } @@ -371,17 +371,17 @@ public class OculusVR implements VRAPI { @Override public Matrix4f getHMDMatrixPoseLeftEye() { - return eyePoses[ovrEye_Left]; + return hmdRelativeEyePoses[ovrEye_Left]; } @Override public Matrix4f getHMDMatrixPoseRightEye() { - return eyePoses[ovrEye_Left]; + return hmdRelativeEyePoses[ovrEye_Left]; } @Override public HmdType getType() { - throw new UnsupportedOperationException(); + return HmdType.OCULUS_RIFT; } public boolean initVRCompositor(boolean set) { From 5df7f80c04076639e856c8c43801f6ab7c29b7fc Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Fri, 29 Sep 2017 11:57:42 +1300 Subject: [PATCH 14/27] Clean up Oculus View Manager update() method --- .../com/jme3/util/VRViewManagerOculus.java | 74 +++++++++---------- 1 file changed, 35 insertions(+), 39 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java index 64fffa988..8b0e7c522 100644 --- a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java +++ b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java @@ -45,6 +45,7 @@ import com.jme3.texture.*; import java.nio.IntBuffer; import java.util.Iterator; +import java.util.Objects; import java.util.logging.Logger; import org.lwjgl.ovr.*; @@ -75,6 +76,7 @@ public class VRViewManagerOculus extends AbstractVRViewManager { this.environment = environment; VRAPI hardware = environment.getVRHardware(); + Objects.requireNonNull(hardware, "Attached VR Hardware cannot be null"); if (!(hardware instanceof OculusVR)) { throw new IllegalStateException("Cannot use Oculus VR view manager on non-Oculus hardware state!"); } @@ -102,49 +104,43 @@ public class VRViewManagerOculus extends AbstractVRViewManager { hardware.updatePose(); // TODO deduplicate - if (environment != null) { - // grab the observer - Object obs = environment.getObserver(); - Quaternion objRot; - Vector3f objPos; - if (obs instanceof Camera) { - objRot = ((Camera) obs).getRotation(); - objPos = ((Camera) obs).getLocation(); - } else { - objRot = ((Spatial) obs).getWorldRotation(); - objPos = ((Spatial) obs).getWorldTranslation(); - } - // grab the hardware handle - VRAPI dev = environment.getVRHardware(); - if (dev != null) { - // update the HMD's position & orientation - dev.getPositionAndOrientation(hmdPos, hmdRot); - if (obs != null) { - // update hmdPos based on obs rotation - finalRotation.set(objRot); - finalRotation.mult(hmdPos, hmdPos); - finalRotation.multLocal(hmdRot); - } + if (environment == null) { + throw new IllegalStateException("This VR view manager is not attached to any VR environment."); + } - finalizeCamera(dev.getHMDVectorPoseLeftEye(), objPos, leftCamera); - finalizeCamera(dev.getHMDVectorPoseRightEye(), objPos, rightCamera); - } else { - leftCamera.setFrame(objPos, objRot); - rightCamera.setFrame(objPos, objRot); - } + // grab the observer + Object obs = environment.getObserver(); + Quaternion objRot; + Vector3f objPos; + if (obs instanceof Camera) { + objRot = ((Camera) obs).getRotation(); + objPos = ((Camera) obs).getLocation(); + } else { + objRot = ((Spatial) obs).getWorldRotation(); + objPos = ((Spatial) obs).getWorldTranslation(); + } - if (environment.hasTraditionalGUIOverlay()) { - // update the mouse? - environment.getVRMouseManager().update(tpf); + // update the HMD's position & orientation + hardware.getPositionAndOrientation(hmdPos, hmdRot); + if (obs != null) { + // update hmdPos based on obs rotation + finalRotation.set(objRot); + finalRotation.mult(hmdPos, hmdPos); + finalRotation.multLocal(hmdRot); + } - // update GUI position? - if (environment.getVRGUIManager().wantsReposition || environment.getVRGUIManager().getPositioningMode() != VRGUIPositioningMode.MANUAL) { - environment.getVRGUIManager().positionGuiNow(tpf); - environment.getVRGUIManager().updateGuiQuadGeometricState(); - } + finalizeCamera(hardware.getHMDVectorPoseLeftEye(), objPos, leftCamera); + finalizeCamera(hardware.getHMDVectorPoseRightEye(), objPos, rightCamera); + + if (environment.hasTraditionalGUIOverlay()) { + // update the mouse? + environment.getVRMouseManager().update(tpf); + + // update GUI position? + if (environment.getVRGUIManager().wantsReposition || environment.getVRGUIManager().getPositioningMode() != VRGUIPositioningMode.MANUAL) { + environment.getVRGUIManager().positionGuiNow(tpf); + environment.getVRGUIManager().updateGuiQuadGeometricState(); } - } else { - throw new IllegalStateException("This VR view manager is not attached to any VR environment."); } } From b1baa26ea13c02d1ab7562a7ebcc5b700148b6d6 Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Fri, 29 Sep 2017 20:17:52 +1300 Subject: [PATCH 15/27] Use two smaller textures for OculusVR, rather than a single large one --- .../main/java/com/jme3/input/vr/OculusVR.java | 59 +++++++++++-------- .../com/jme3/util/VRViewManagerOculus.java | 9 ++- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java index 65831d6f6..513d725a3 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java @@ -113,12 +113,12 @@ public class OculusVR implements VRAPI { /** * Chain texture set thing. */ - private long chain; + private long chains[]; /** * Frame buffers we can draw into. */ - private FrameBuffer framebuffers[]; + private FrameBuffer framebuffers[][]; public OculusVR(VREnvironment environment) { this.environment = environment; @@ -287,9 +287,11 @@ public class OculusVR implements VRAPI { // fovPorts: contents are managed by LibOVR, no need to do anything. // Check if we've set up rendering - if so, clean that up. - if (chain != 0) { + if (chains != null) { // Destroy our set of huge buffer images. - ovr_DestroyTextureSwapChain(session, chain); + for (long chain : chains) { + ovr_DestroyTextureSwapChain(session, chain); + } // Free up the layer layer0.free(); @@ -391,7 +393,10 @@ public class OculusVR implements VRAPI { findHMDTextureSize(); setupLayers(); - setupFramebuffers(); + + framebuffers = new FrameBuffer[2][]; + for (int eye = 0; eye < 2; eye++) + setupFramebuffers(eye); // TODO move initialization code here from VRViewManagerOculus return true; @@ -427,14 +432,21 @@ public class OculusVR implements VRAPI { ovr_GetFovTextureSize(session, ovrEye_Right, fovPorts[ovrEye_Right], pixelScaling, rightTextureSize); System.out.println("rightTextureSize W=" + rightTextureSize.w() + ", H=" + rightTextureSize.h()); - textureW = leftTextureSize.w() + rightTextureSize.w(); - textureH = Math.max(leftTextureSize.h(), rightTextureSize.h()); + if (leftTextureSize.w() != rightTextureSize.w()) { + throw new IllegalStateException("Texture sizes do not match [horizontal]"); + } + if (leftTextureSize.h() != rightTextureSize.h()) { + throw new IllegalStateException("Texture sizes do not match [vertical]"); + } + + textureW = leftTextureSize.w(); + textureH = leftTextureSize.h(); leftTextureSize.free(); rightTextureSize.free(); } - private PointerBuffer setupTextureChain() { + private long setupTextureChain() { // Set up the information for the texture buffer chain thing OVRTextureSwapChainDesc swapChainDesc = OVRTextureSwapChainDesc.calloc() .Type(ovrTexture_2D) @@ -451,29 +463,30 @@ public class OculusVR implements VRAPI { if (OVRGL.ovr_CreateTextureSwapChainGL(session, swapChainDesc, textureSetPB) != ovrSuccess) { throw new RuntimeException("Failed to create Swap Texture Set"); } - chain = textureSetPB.get(0); swapChainDesc.free(); System.out.println("done chain creation"); - return textureSetPB; + return textureSetPB.get(); // TODO is this a memory leak? } public void setupLayers() { - PointerBuffer chainPtr = setupTextureChain(); - //Layers layer0 = OVRLayerEyeFov.calloc(); layer0.Header().Type(ovrLayerType_EyeFov); layer0.Header().Flags(ovrLayerFlag_TextureOriginAtBottomLeft); + chains = new long[2]; for (int eye = 0; eye < 2; eye++) { + long eyeChain = setupTextureChain(); + chains[eye] = eyeChain; + OVRRecti viewport = OVRRecti.calloc(); viewport.Pos().x(0); viewport.Pos().y(0); viewport.Size().w(textureW); viewport.Size().h(textureH); - layer0.ColorTexture(chainPtr); + layer0.ColorTexture(eye, eyeChain); layer0.Viewport(eye, viewport); layer0.Fov(eye, fovPorts[eye]); @@ -486,22 +499,22 @@ public class OculusVR implements VRAPI { } /** - * Create framebuffers bound to each of the eye textures + * Create a framebuffer for an eye. */ - public void setupFramebuffers() { + public void setupFramebuffers(int eye) { // Find the chain length IntBuffer length = BufferUtils.createIntBuffer(1); - ovr_GetTextureSwapChainLength(session, chain, length); + ovr_GetTextureSwapChainLength(session, chains[eye], length); int chainLength = length.get(); System.out.println("chain length=" + chainLength); // Create the frame buffers - framebuffers = new FrameBuffer[chainLength]; + framebuffers[eye] = new FrameBuffer[chainLength]; for (int i = 0; i < chainLength; i++) { // find the GL texture ID for this texture IntBuffer textureIdB = BufferUtils.createIntBuffer(1); - OVRGL.ovr_GetTextureSwapChainBufferGL(session, chain, i, textureIdB); + OVRGL.ovr_GetTextureSwapChainBufferGL(session, chains[eye], i, textureIdB); int textureId = textureIdB.get(); // TODO less hacky way of getting our texture into JMonkeyEngine @@ -517,7 +530,7 @@ public class OculusVR implements VRAPI { buffer.setDepthBuffer(Image.Format.Depth); buffer.setColorTexture(tex); - framebuffers[i] = buffer; + framebuffers[eye][i] = buffer; } } @@ -583,12 +596,12 @@ public class OculusVR implements VRAPI { return session; } - public long getChain() { - return chain; + public long getChain(int eye) { + return chains[eye]; } - public FrameBuffer[] getFramebuffers() { - return framebuffers; + public FrameBuffer[] getFramebuffers(int eye) { + return framebuffers[eye]; } public PointerBuffer getLayers() { diff --git a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java index 8b0e7c522..fba104238 100644 --- a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java +++ b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java @@ -169,10 +169,11 @@ public class VRViewManagerOculus extends AbstractVRViewManager { hardware.getLayer0().RenderPose(eye, eyePose); IntBuffer currentIndexB = BufferUtils.createIntBuffer(1); - ovr_GetTextureSwapChainCurrentIndex(session(), hardware.getChain(), currentIndexB); + ovr_GetTextureSwapChainCurrentIndex(session(), hardware.getChain(eye), currentIndexB); int index = currentIndexB.get(); - (eye == ovrEye_Left ? leftViewPort : rightViewPort).setOutputFrameBuffer(hardware.getFramebuffers()[index]); + // FIXME eyes inverted + (eye != ovrEye_Left ? leftViewPort : rightViewPort).setOutputFrameBuffer(hardware.getFramebuffers(eye)[index]); } // Now the game will render into the buffers given to us by LibOVR @@ -181,7 +182,9 @@ public class VRViewManagerOculus extends AbstractVRViewManager { @Override public void postRender() { // We're done with our textures now - the game is done drawing into them. - ovr_CommitTextureSwapChain(session(), hardware.getChain()); + for (int eye = 0; eye < 2; eye++) { + ovr_CommitTextureSwapChain(session(), hardware.getChain(eye)); + } // Send the result to the HMD int result = ovr_SubmitFrame(session(), 0, null, hardware.getLayers()); From da52de7f7ff6b476a594efbb02940b8f1509a7f0 Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Fri, 29 Sep 2017 23:14:11 +1300 Subject: [PATCH 16/27] OculusVR: Get basic projections working (mostly), however, VR cameras still don't work properly --- .../main/java/com/jme3/input/vr/OculusVR.java | 35 +++++++++++++++--- .../com/jme3/util/VRViewManagerOculus.java | 37 +++---------------- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java index 513d725a3..ad7ca2d79 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java @@ -26,6 +26,13 @@ import static org.lwjgl.system.MemoryUtil.*; /** * Oculus VR (LibOVR 1.3.0) Native support. + *

+ * A few notes about the Oculus coordinate system: + *

    + *
  • Matrices should be transposed
  • + *
  • Quaternions should be inverted
  • + *
  • Vectors should have their X and Z axes flipped, but apparently not Y.
  • + *
* * @author Campbell Suter */ @@ -219,7 +226,6 @@ public class OculusVR implements VRAPI { 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 } @@ -332,7 +338,7 @@ public class OculusVR implements VRAPI { @Override public Quaternion getOrientation() { - return quatO2J(headPose.Orientation(), new Quaternion()); + return quatO2J(headPose.Orientation(), new Quaternion()).inverseLocal(); } @Override @@ -342,18 +348,31 @@ public class OculusVR implements VRAPI { @Override public void getPositionAndOrientation(Vector3f storePos, Quaternion storeRot) { - vecO2J(headPose.Position(), storePos); - quatO2J(headPose.Orientation(), storeRot); + storePos.set(getPosition()); + storeRot.set(getOrientation()); + } + + private Matrix4f calculateProjection(int eye, Camera cam) { + Matrix4f mat = new Matrix4f(); + + // Get LibOVR to find the correct projection + OVRUtil.ovrMatrix4f_Projection(fovPorts[eye], cam.getFrustumNear(), cam.getFrustumFar(), OVRUtil.ovrProjection_None, projections[eye]); + + matrixO2J(projections[eye], mat); + + mat.transposeLocal(); // Apparently LibOVR has a different coordinate set - yay for us. + + return mat; } @Override public Matrix4f getHMDMatrixProjectionLeftEye(Camera cam) { - return matrixO2J(projections[ovrEye_Left], new Matrix4f()); + return calculateProjection(ovrEye_Left, cam); } @Override public Matrix4f getHMDMatrixProjectionRightEye(Camera cam) { - return matrixO2J(projections[ovrEye_Right], new Matrix4f()); + return calculateProjection(ovrEye_Right, cam); } @Override @@ -612,6 +631,10 @@ public class OculusVR implements VRAPI { return layer0; } + public OVRFovPort getFovPort() { + return fovPorts[ovrEye_Left]; // TODO checking the left and right eyes match + } + public OVRPosef[] getEyePosesPtr() { return eyePosesPtr; } diff --git a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java index fba104238..2bd756d9c 100644 --- a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java +++ b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java @@ -34,10 +34,7 @@ package com.jme3.util; import com.jme3.app.VREnvironment; import com.jme3.input.vr.OculusVR; import com.jme3.input.vr.VRAPI; -import com.jme3.math.ColorRGBA; -import com.jme3.math.Quaternion; -import com.jme3.math.Vector2f; -import com.jme3.math.Vector3f; +import com.jme3.math.*; import com.jme3.renderer.Camera; import com.jme3.renderer.ViewPort; import com.jme3.scene.Spatial; @@ -213,16 +210,14 @@ public class VRViewManagerOculus extends AbstractVRViewManager { // restore frustrum on distortion scene cam, if needed if (environment.isInstanceRendering()) { leftCamera = origCam; - } else if (environment.compositorAllowed() == false) { - origCam.setFrustumFar(100f); - origCam.setFrustumNear(1f); - leftCamera = origCam.clone(); - prepareCameraSize(origCam, 2f); } else { leftCamera = origCam.clone(); } - getLeftCamera().setFrustumPerspective(environment.getDefaultFOV(), environment.getDefaultAspect(), fNear, fFar); + OVRFovPort fp = hardware.getFovPort(); + float hFov = fp.LeftTan() + fp.RightTan(); + float vFov = fp.UpTan() + fp.DownTan(); + getLeftCamera().setFrustumPerspective(hFov / FastMath.TWO_PI * 360, vFov / hFov, fNear, fFar); prepareCameraSize(getLeftCamera(), 1f); if (environment.getVRHardware() != null) { @@ -238,33 +233,13 @@ public class VRViewManagerOculus extends AbstractVRViewManager { } rightViewPort = setupViewBuffers(getRightCamera(), RIGHT_VIEW_NAME); } else if (environment.getApplication() != null) { - - LOG.severe("THIS CODE NEED CHANGES !!!"); - leftViewPort = environment.getApplication().getViewPort(); - //leftViewport.attachScene(app.getRootNode()); - rightCamera = getLeftCamera().clone(); - if (environment.getVRHardware() != null) { - getRightCamera().setProjectionMatrix(environment.getVRHardware().getHMDMatrixProjectionRightEye(getRightCamera())); - } - - org.lwjgl.opengl.GL11.glEnable(org.lwjgl.opengl.GL30.GL_CLIP_DISTANCE0); - - //FIXME: [jme-vr] Fix with JMonkey next release - //RenderManager._VRInstancing_RightCamProjection = camRight.getViewProjectionMatrix(); - // TODO: Add LibOVR support - // setupFinalFullTexture(environment.getApplication().getViewPort().getCamera()); + throw new UnsupportedOperationException("Not yet implemented!"); } else { throw new IllegalStateException("This VR environment is not attached to any application."); } // setup gui environment.getVRGUIManager().setupGui(getLeftCamera(), getRightCamera(), getLeftViewPort(), getRightViewPort()); - - if (environment.getVRHardware() != null) { - // call these to cache the results internally - environment.getVRHardware().getHMDMatrixPoseLeftEye(); - environment.getVRHardware().getHMDMatrixPoseRightEye(); - } } else { throw new IllegalStateException("This VR view manager is not attached to any VR environment."); } From b6b1687450547fc5865cb3abede17b805e7b42e8 Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Sun, 1 Oct 2017 18:17:41 +1300 Subject: [PATCH 17/27] OculusVR: Fix eye-texture sizing issue, producing correct FOV --- jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java index ad7ca2d79..de2914d6f 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java @@ -252,6 +252,9 @@ public class OculusVR implements VRAPI { System.out.println("step 7 - recenter"); ovr_RecenterTrackingOrigin(session); + // Do this so others relying on our texture size get it correct. + findHMDTextureSize(); + // throw new UnsupportedOperationException("Not yet implemented!"); return true; } @@ -327,8 +330,11 @@ public class OculusVR implements VRAPI { @Override public void getRenderSize(Vector2f store) { - store.x = resolutionW; - store.y = resolutionH; + if(!isInitialized()) { + throw new IllegalStateException("Cannot call getRenderSize() before initialized!"); + } + store.x = textureW; + store.y = textureH; } @Override @@ -410,7 +416,6 @@ public class OculusVR implements VRAPI { throw new UnsupportedOperationException("Cannot use LibOVR without compositor!"); } - findHMDTextureSize(); setupLayers(); framebuffers = new FrameBuffer[2][]; From d7fc7160654a7e77f642d0f0a7bf247ca8ef304c Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Sun, 1 Oct 2017 18:26:22 +1300 Subject: [PATCH 18/27] Oculus: Initialize GUI to prevent startup crashes --- .../src/main/java/com/jme3/util/VRViewManagerOculus.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java index 2bd756d9c..ac4a793b9 100644 --- a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java +++ b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java @@ -88,6 +88,15 @@ public class VRViewManagerOculus extends AbstractVRViewManager { @Override public void initialize() { setupCamerasAndViews(); + + if (environment.hasTraditionalGUIOverlay()) { + + environment.getVRMouseManager().initialize(); + + // update the pose to position the gui correctly on start + update(0f); + environment.getVRGUIManager().positionGui(); + } } private long session() { From 760277f61d14c24b71ce224ffe6124ac55c9f660 Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Sun, 1 Oct 2017 23:15:17 +1300 Subject: [PATCH 19/27] Make OculusVR.getHMDVectorPoseLeftEye return the HMD relative, not world relative, eye positions. --- .../main/java/com/jme3/input/vr/OculusVR.java | 51 +++++++------------ .../com/jme3/util/VRViewManagerOculus.java | 17 +++++-- 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java index de2914d6f..5e6b93f90 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java @@ -95,14 +95,11 @@ public class OculusVR implements VRAPI { private final Matrix4f[] hmdRelativeEyePoses = new Matrix4f[2]; /** - * The eye poses relative to the world, as used during rendering. - */ - private final OVRPosef eyePosesPtr[] = new OVRPosef[2]; - - /** - * The eye positions relative to the world, as used by jME. + * Store the positions for each eye, relative to the HMD. + * + * @see #getHMDVectorPoseLeftEye() */ - private final Vector3f eyePositions[] = new Vector3f[2]; + private final Vector3f[] hmdRelativeEyePositions = new Vector3f[2]; /** * The position and orientation of the user's head. @@ -227,6 +224,9 @@ public class OculusVR implements VRAPI { for (int eye = 0; eye < 2; eye++) { projections[eye] = OVRMatrix4f.malloc(); //1.3 was right handed, now none flag + + hmdRelativeEyePoses[eye] = new Matrix4f(); + hmdRelativeEyePositions[eye] = new Vector3f(); } // step 6 - render desc @@ -240,12 +240,11 @@ public class OculusVR implements VRAPI { OVRPosef pose = eyeRenderDesc[eye].HmdToEyePose(); - Matrix4f jPose = new Matrix4f(); - jPose.setTranslation(vecO2J(pose.Position(), new Vector3f())); - jPose.setRotationQuaternion(quatO2J(pose.Orientation(), new Quaternion())); + vecO2J(pose.Position(), hmdRelativeEyePositions[eye]); - hmdRelativeEyePoses[eye] = jPose; - eyePositions[eye] = new Vector3f(); // Set the absolute position up for later. + hmdRelativeEyePoses[eye].loadIdentity(); + hmdRelativeEyePoses[eye].setTranslation(hmdRelativeEyePositions[eye]); + hmdRelativeEyePoses[eye].setRotationQuaternion(quatO2J(pose.Orientation(), new Quaternion())); } // step 7 - recenter @@ -268,22 +267,6 @@ public class OculusVR implements VRAPI { //get head pose headPose = hmdState.HeadPose().ThePose(); hmdState.free(); - - //build view offsets struct - OVRPosef.Buffer hmdToEyeOffsets = OVRPosef.calloc(2); - hmdToEyeOffsets.put(0, eyeRenderDesc[ovrEye_Left].HmdToEyePose()); - hmdToEyeOffsets.put(1, eyeRenderDesc[ovrEye_Right].HmdToEyePose()); - - //calculate eye poses - OVRPosef.Buffer outEyePoses = OVRPosef.create(2); - OVRUtil.ovr_CalcEyePoses(headPose, hmdToEyeOffsets, outEyePoses); - hmdToEyeOffsets.free(); - eyePosesPtr[ovrEye_Left] = outEyePoses.get(0); - eyePosesPtr[ovrEye_Right] = outEyePoses.get(1); - - for (int i = 0; i < eyePosesPtr.length; i++) { - vecO2J(eyePosesPtr[i].Position(), eyePositions[i]); - } } @Override @@ -383,12 +366,12 @@ public class OculusVR implements VRAPI { @Override public Vector3f getHMDVectorPoseLeftEye() { - return eyePositions[ovrEye_Left]; + return hmdRelativeEyePositions[ovrEye_Left]; } @Override public Vector3f getHMDVectorPoseRightEye() { - return eyePositions[ovrEye_Right]; + return hmdRelativeEyePositions[ovrEye_Right]; } @Override @@ -640,8 +623,12 @@ public class OculusVR implements VRAPI { return fovPorts[ovrEye_Left]; // TODO checking the left and right eyes match } - public OVRPosef[] getEyePosesPtr() { - return eyePosesPtr; + public OVRPosef getHeadPose() { + return headPose; + } + + public OVRPosef getEyePose(int eye) { + return eyeRenderDesc[eye].HmdToEyePose(); } } diff --git a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java index ac4a793b9..20968aa1f 100644 --- a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java +++ b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java @@ -169,11 +169,20 @@ public class VRViewManagerOculus extends AbstractVRViewManager { @Override public void render() { - for (int eye = 0; eye < 2; eye++) { - // TODO do we need this? Don't we set the camera positions ourselves? - OVRPosef eyePose = hardware.getEyePosesPtr()[eye]; - hardware.getLayer0().RenderPose(eye, eyePose); + // Calculate the render pose (translation/rotation) for each eye. + // LibOVR takes the difference between this and the real position of each eye at display time + // to apply AZW (timewarp). + + OVRPosef.Buffer hmdToEyeOffsets = OVRPosef.calloc(2); + hmdToEyeOffsets.put(0, hardware.getEyePose(ovrEye_Left)); + hmdToEyeOffsets.put(1, hardware.getEyePose(ovrEye_Right)); + + //calculate eye poses + OVRUtil.ovr_CalcEyePoses(hardware.getHeadPose(), hmdToEyeOffsets, hardware.getLayer0().RenderPose()); + hmdToEyeOffsets.free(); + + for (int eye = 0; eye < 2; eye++) { IntBuffer currentIndexB = BufferUtils.createIntBuffer(1); ovr_GetTextureSwapChainCurrentIndex(session(), hardware.getChain(eye), currentIndexB); int index = currentIndexB.get(); From 9747c556ffaa65a36138aa7c9647c4c1271d3d9a Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Sun, 1 Oct 2017 23:45:43 +1300 Subject: [PATCH 20/27] OculusVR: Use correct coordinate space conversions, looking around now works --- .../main/java/com/jme3/input/vr/OculusVR.java | 20 ++++++++++++------- .../com/jme3/util/VRViewManagerOculus.java | 5 +++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java index 5e6b93f90..56d49f705 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java @@ -327,7 +327,7 @@ public class OculusVR implements VRAPI { @Override public Quaternion getOrientation() { - return quatO2J(headPose.Orientation(), new Quaternion()).inverseLocal(); + return quatO2J(headPose.Orientation(), new Quaternion()); } @Override @@ -349,8 +349,6 @@ public class OculusVR implements VRAPI { matrixO2J(projections[eye], mat); - mat.transposeLocal(); // Apparently LibOVR has a different coordinate set - yay for us. - return mat; } @@ -552,6 +550,8 @@ public class OculusVR implements VRAPI { * @return The {@code to} argument. */ public static Matrix4f matrixO2J(OVRMatrix4f from, Matrix4f to) { + to.loadIdentity(); // For the additional columns (unless I'm badly misunderstanding matricies) + for (int x = 0; x < 4; x++) { for (int y = 0; y < 4; y++) { float val = from.M(x + y * 4); // TODO verify this @@ -559,6 +559,8 @@ public class OculusVR implements VRAPI { } } + to.transposeLocal(); // jME vs LibOVR coordinate spaces - Yay! + return to; } @@ -570,13 +572,16 @@ public class OculusVR implements VRAPI { * @return The {@code to} argument. */ public static Quaternion quatO2J(OVRQuatf from, Quaternion to) { + // jME and LibOVR do their coordinate spaces differently for rotations, so flip Y and W (thanks, jMonkeyVR). to.set( from.x(), - from.y(), + -from.y(), from.z(), - from.w() + -from.w() ); + to.normalizeLocal(); + return to; } @@ -588,10 +593,11 @@ public class OculusVR implements VRAPI { * @return The {@code to} argument. */ public static Vector3f vecO2J(OVRVector3f from, Vector3f to) { + // jME and LibOVR disagree on which way X and Z is, too. to.set( - from.x(), + -from.x(), from.y(), - from.z() + -from.z() ); return to; diff --git a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java index 20968aa1f..4711963f6 100644 --- a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java +++ b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java @@ -187,8 +187,9 @@ public class VRViewManagerOculus extends AbstractVRViewManager { ovr_GetTextureSwapChainCurrentIndex(session(), hardware.getChain(eye), currentIndexB); int index = currentIndexB.get(); - // FIXME eyes inverted - (eye != ovrEye_Left ? leftViewPort : rightViewPort).setOutputFrameBuffer(hardware.getFramebuffers(eye)[index]); + // Constantly (each frame) rotating through a series of + // frame buffers, so make sure we write into the correct one. + (eye == ovrEye_Left ? leftViewPort : rightViewPort).setOutputFrameBuffer(hardware.getFramebuffers(eye)[index]); } // Now the game will render into the buffers given to us by LibOVR From 7b30f69c2a3780e2845f69952d1f8eab74aa03c8 Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Thu, 5 Oct 2017 11:56:00 +1300 Subject: [PATCH 21/27] Fix mirror window displaying blank for Oculus Rift --- jme3-vr/src/main/java/com/jme3/app/VRAppState.java | 7 ++++++- .../main/java/com/jme3/util/VRViewManagerOculus.java | 10 ++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/jme3-vr/src/main/java/com/jme3/app/VRAppState.java b/jme3-vr/src/main/java/com/jme3/app/VRAppState.java index 806f2cc42..6d29dbb24 100644 --- a/jme3-vr/src/main/java/com/jme3/app/VRAppState.java +++ b/jme3-vr/src/main/java/com/jme3/app/VRAppState.java @@ -34,6 +34,7 @@ package com.jme3.app; import com.jme3.app.Application; import com.jme3.app.state.AbstractAppState; import com.jme3.app.state.AppStateManager; +import com.jme3.input.vr.OculusVR; import com.jme3.input.vr.VRAPI; import com.jme3.input.vr.VRInputAPI; import com.jme3.math.ColorRGBA; @@ -608,7 +609,11 @@ public class VRAppState extends AbstractAppState { settings.setFrequency(environment.getVRHardware().getDisplayFrequency()); settings.setFullscreen(false); settings.setVSync(false); // stop vsyncing on primary monitor! - settings.setSwapBuffers(environment.isSwapBuffers()); + + // TODO: Is this preventing desktop display on _ALL_ HMDs? + if(!(getVRHardware() instanceof OculusVR)) { + settings.setSwapBuffers(environment.isSwapBuffers()); + } } // Updating application settings diff --git a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java index 4711963f6..181f3640c 100644 --- a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java +++ b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java @@ -135,9 +135,19 @@ public class VRViewManagerOculus extends AbstractVRViewManager { finalRotation.multLocal(hmdRot); } + // Update both eye cameras finalizeCamera(hardware.getHMDVectorPoseLeftEye(), objPos, leftCamera); finalizeCamera(hardware.getHMDVectorPoseRightEye(), objPos, rightCamera); + // Update the main camera, so it shows the same basic view the HMD is getting + // TODO: Do this in VRAppState, so it works on all HMDs. + // I only have a Rift, so I can't test it on anything else. + if(!environment.isInstanceRendering()) { // We use the app camera as the left camera here + // TODO: Double up on rendering and use one eye, to reduce GPU load rendering the scene again. + // TODO: Snip at the image to remove the distorted corners from a very high FOV. + finalizeCamera(Vector3f.ZERO, objPos, environment.getApplication().getCamera()); + } + if (environment.hasTraditionalGUIOverlay()) { // update the mouse? environment.getVRMouseManager().update(tpf); From fcccdd75a59ca54f268d3a0536803529e521f194 Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Thu, 5 Oct 2017 11:57:14 +1300 Subject: [PATCH 22/27] Don't create unnecessary FrameBuffer for OculusVR view manager --- .../java/com/jme3/util/VRViewManagerOculus.java | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java index 181f3640c..53a00aabd 100644 --- a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java +++ b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOculus.java @@ -319,19 +319,6 @@ public class VRViewManagerOculus extends AbstractVRViewManager { // TODO this function is identical to that in VRViewManagerOpenVR; merge the two. if (environment != null) { if (environment.getApplication() != null) { - // create offscreen framebuffer - FrameBuffer offBufferLeft = new FrameBuffer(cam.getWidth(), cam.getHeight(), 1); - //offBufferLeft.setSrgb(true); - - //setup framebuffer's texture - Texture2D offTex = new Texture2D(cam.getWidth(), cam.getHeight(), Image.Format.RGBA8); - offTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); - offTex.setMagFilter(Texture.MagFilter.Bilinear); - - //setup framebuffer to use texture - offBufferLeft.setDepthBuffer(Image.Format.Depth); - offBufferLeft.setColorTexture(offTex); - ViewPort viewPort = environment.getApplication().getRenderManager().createPreView(viewName, cam); viewPort.setClearFlags(true, true, true); viewPort.setBackgroundColor(ColorRGBA.Black); @@ -341,8 +328,7 @@ public class VRViewManagerOculus extends AbstractVRViewManager { viewPort.attachScene(spatialIter.next()); } - //set viewport to render to offscreen framebuffer - viewPort.setOutputFrameBuffer(offBufferLeft); + // The viewbuffer to render into will be set during prerender. return viewPort; } else { throw new IllegalStateException("This VR environment is not attached to any application."); From c26316d81c0f08a12fc2b166012633fd16e55930 Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Fri, 6 Oct 2017 20:21:22 +1300 Subject: [PATCH 23/27] Add Oculus Touch mappings to VRInputType --- .../java/com/jme3/input/vr/VRInputType.java | 100 +++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/VRInputType.java b/jme3-vr/src/main/java/com/jme3/input/vr/VRInputType.java index aca571f35..2d529cf77 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/VRInputType.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/VRInputType.java @@ -1,5 +1,7 @@ package com.jme3.input.vr; +import static org.lwjgl.ovr.OVR.*; // For the button constants + /** * The type of a VR input. This enumeration enables to determine which part of the VR device is involved within input callback. * @author reden - phr00t - https://github.com/phr00t @@ -26,7 +28,103 @@ public enum VRInputType { /** * an HTC vive menu button (about Vive controller). */ - ViveMenuButton(3); + ViveMenuButton(3), + + /** + * The thumbstick on the Oculus Touch controllers. + * + * Unlike the Vive controllers where the touchpad is commonly used + * as a virtual DPad, you should avoid using the thumbstick for purposes + * that do not require analog input. + */ + OculusThumbstickAxis(0), + + /** + * The trigger button on the Oculus Touch controllers. + * + * This is the button under the user's index finger, and should not be used to + * pick up objects. See the + * Oculus Developer documentation. + */ + OculusTriggerAxis(0), + + /** + * The 'grab' button on the Oculus Touch controllers. + * + * This button should only (unless you have a compelling reason otherwise) be used to pick up objects. + */ + OculusGripAxis(0), + + /** + * The upper buttons on the Oculus Touch controllers - B on the right controller, and Y on the left. + */ + OculusTopButton(ovrButton_B | ovrButton_Y), + + /** + * The lower (not counting menu) buttons on the Oculus Touch + * controllers - A on the right controller, and X on the left. + */ + OculusBottomButton(ovrButton_A | ovrButton_X), + + /** + * The 'click' button on the Oculus Touch thumbsticks. + */ + OculusThumbstickButton(ovrButton_LThumb | ovrButton_RThumb), + + /** + * The game-usable menu button, under and to the left of the 'X' button on the left controller. + * + * Most games use this to pause - it preferably should be used for at least that purpose, and is + * uncomfortable to rest your thumb on (in games where you suddenly have to pause/open a menu). + */ + OculusMenuButton(ovrButton_Enter), + + /** + * The capacitive touch sensors on the top buttons (Y and B) of the Oculus Touch. + */ + OculusTopTouch(ovrTouch_B | ovrTouch_Y), + + /** + * The capacitive touch sensors on the lower buttons (X and A) of the Oculus Touch. + */ + OculusBottomTouch(ovrTouch_A | ovrTouch_X), + + /** + * The capacitive touch sensors on the thumbsticks of the Oculus Touch. + */ + OculusThumbstickTouch(ovrTouch_LThumb | ovrTouch_RThumb), + + /** + * The capacitive touch sensors on the thumbrests of the Oculus Touch - this is a textured pad + * on the Oculus Touch controller next to the ABXY buttons for users to reset their thumbs on. + * + * While it probably goes without saying, only use this for gesture support and do not bind game + * elements to it. + */ + OculusThumbrestTouch(ovrTouch_LThumbRest | ovrTouch_RThumbRest), + + /** + * The state of a software calculation based on the capacitive touch sensor values that determine if + * the user has lifted their thumb off the controller, and can be used for gesture support. + * + * This should be used instead of calculating this yourself based on the touch results of all the other + * parts of the controller. + */ + OculusThumbUp(ovrTouch_LThumbUp | ovrTouch_RThumbUp), + + /** + * Is the user resting their finger on the trigger of an Oculus Touch controller? + */ + OculusIndexTouch(ovrTouch_LIndexPointing | ovrTouch_RIndexPointing), + + /** + * Is the user pointing their finger forwards, as if to press a button? + * + * This is internally calculated from proximity and filtering is applied - it should be used rather + * than !OculusIndexTouch, as it will probably lead to better results. + */ + OculusIndexPointing(ovrTouch_LIndexPointing | ovrTouch_RIndexPointing); /** * The value that codes the input type. From 2464dcd17add1c097bf1e9224cf798f9e01f2bd1 Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Fri, 6 Oct 2017 21:56:03 +1300 Subject: [PATCH 24/27] Add OculusVRInput --- .../main/java/com/jme3/input/vr/OculusVR.java | 43 +- .../java/com/jme3/input/vr/OculusVRInput.java | 367 ++++++++++++++++++ 2 files changed, 401 insertions(+), 9 deletions(-) create mode 100644 jme3-vr/src/main/java/com/jme3/input/vr/OculusVRInput.java diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java index 56d49f705..32ba94356 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java @@ -101,11 +101,21 @@ public class OculusVR implements VRAPI { */ private final Vector3f[] hmdRelativeEyePositions = new Vector3f[2]; + /** + * The current state of the tracked components (HMD, touch) + */ + private OVRTrackingState trackingState; + /** * The position and orientation of the user's head. */ private OVRPosef headPose; + /** + * The state of the Touch controllers. + */ + private OculusVRInput input; + // The size of the texture drawn onto the HMD private int textureW; private int textureH; @@ -129,8 +139,8 @@ public class OculusVR implements VRAPI { } @Override - public OpenVRInput getVRinput() { - throw new UnsupportedOperationException(); + public OculusVRInput getVRinput() { + return input; } @Override @@ -184,7 +194,7 @@ public class OculusVR implements VRAPI { 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; + return false; // TODO fix memory leak - destroy() is not called } session = pHmd.get(0); memFree(pHmd); @@ -198,7 +208,7 @@ public class OculusVR implements VRAPI { 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; + return false; // TODO fix memory leak - destroy() is not called } resolutionW = hmdDesc.Resolution().w(); @@ -206,7 +216,7 @@ public class OculusVR implements VRAPI { System.out.println("resolution W=" + resolutionW + ", H=" + resolutionH); if (resolutionW == 0) { System.out.println("Huh - width=0"); - return false; + return false; // TODO fix memory leak - destroy() is not called } // FOV @@ -254,6 +264,12 @@ public class OculusVR implements VRAPI { // Do this so others relying on our texture size get it correct. findHMDTextureSize(); + // Set up the tracking system + trackingState = OVRTrackingState.malloc(); + + // Set up the input + input = new OculusVRInput(this, session, sessionStatus, trackingState); + // throw new UnsupportedOperationException("Not yet implemented!"); return true; } @@ -261,12 +277,13 @@ public class OculusVR implements VRAPI { @Override public void updatePose() { double ftiming = ovr_GetPredictedDisplayTime(session, 0); - OVRTrackingState hmdState = OVRTrackingState.malloc(); - ovr_GetTrackingState(session, ftiming, true, hmdState); + ovr_GetTrackingState(session, ftiming, true, trackingState); + ovr_GetSessionStatus(session, sessionStatus); + + input.updateControllerStates(); //get head pose - headPose = hmdState.HeadPose().ThePose(); - hmdState.free(); + headPose = trackingState.HeadPose().ThePose(); } @Override @@ -278,6 +295,9 @@ public class OculusVR implements VRAPI { public void destroy() { // fovPorts: contents are managed by LibOVR, no need to do anything. + // Clean up the input + input.dispose(); + // Check if we've set up rendering - if so, clean that up. if (chains != null) { // Destroy our set of huge buffer images. @@ -299,6 +319,7 @@ public class OculusVR implements VRAPI { } hmdDesc.free(); + trackingState.free(); sessionStatus.free(); // Wrap everything up @@ -636,6 +657,10 @@ public class OculusVR implements VRAPI { public OVRPosef getEyePose(int eye) { return eyeRenderDesc[eye].HmdToEyePose(); } + + public VREnvironment getEnvironment() { + return environment; + } } /* vim: set ts=4 softtabstop=0 sw=4 expandtab: */ diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVRInput.java b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVRInput.java new file mode 100644 index 000000000..9016850bc --- /dev/null +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVRInput.java @@ -0,0 +1,367 @@ +package com.jme3.input.vr; + +import com.jme3.app.VREnvironment; +import com.jme3.math.*; +import com.jme3.renderer.Camera; +import com.jme3.scene.Spatial; +import com.jme3.util.VRViewManagerOculus; +import org.lwjgl.ovr.*; + +import static org.lwjgl.ovr.OVR.*; + +public class OculusVRInput implements VRInputAPI { + // State control + private final OVRInputState inputState; + private final OVRSessionStatus sessionStatus; + private final OVRTrackingState trackingState; + private final OculusVR hardware; + private long session; + + // Setup values + private float axisMultiplier = 1; + + // Cached stuff + private int buttons, touch; + + // Used to calculate sinceLastCall stuff + private int lastButtons, lastTouch; + private final Vector2f[][] lastAxises; + + /** + * The state data (linear and angular velocity and acceleration) for each hand + */ + private OVRPoseStatef[] handStates; + + /** + * The position and orientation of the Touch controllers. + */ + private OVRPosef[] handPoses; + + /** + * The object forms of the tracked controllers. + */ + private final OculusController[] controllers = { + new OculusController(0), + new OculusController(1) + }; + + public OculusVRInput(OculusVR hardware, long session, + OVRSessionStatus sessionStatus, OVRTrackingState trackingState) { + this.hardware = hardware; + this.session = session; + this.sessionStatus = sessionStatus; + this.trackingState = trackingState; + + inputState = OVRInputState.calloc(); + + handStates = new OVRPoseStatef[ovrHand_Count]; + handPoses = new OVRPosef[handStates.length]; + lastAxises = new Vector2f[handStates.length][3]; // trigger+grab+thumbstick for each hand. + } + + public void dispose() { + inputState.free(); + session = 0; // Crashing > undefined behaviour if this object is incorrectly accessed again. + } + + @Override + public void updateControllerStates() { + // Handle buttons, axies + ovr_GetInputState(session, ovrControllerType_Touch, inputState); + buttons = inputState.Buttons(); + touch = inputState.Touches(); + + // Get the touch controller poses + // TODO what if no touch controllers are available? + for (int hand = 0; hand < handPoses.length; hand++) { + handStates[hand] = trackingState.HandPoses(hand); + handPoses[hand] = handStates[hand].ThePose(); + } + } + + private Vector3f cv(OVRVector3f in) { + // TODO do we want to reuse vectors rather than making new ones? + // TODO OpenVRInput does this, but it will probably cause some bugs. + return OculusVR.vecO2J(in, new Vector3f()); // This also fixes the coordinate space transform issues. + } + + private Vector2f cv(OVRVector2f in) { + // TODO do we want to reuse vectors rather than making new ones? + // TODO OpenVRInput does this, but it will probably cause some bugs. + return new Vector2f(in.x(), in.y()); + } + + private Quaternion cq(OVRQuatf in) { + // TODO do we want to reuse quaternions rather than making new ones? + // TODO OpenVRInput does this, but it will probably cause some bugs. + return OculusVR.quatO2J(in, new Quaternion()); // This also fixes the coordinate space transform issues. + } + + private Vector2f axis(float input) { + // See above comments about reusing vectors + return new Vector2f(input, input); + } + + // Tracking (position, rotation, velocity, status) + + @Override + public Vector3f getPosition(int index) { + return cv(handPoses[index].Position()); + } + + @Override + public Vector3f getVelocity(int controllerIndex) { + return cv(handStates[controllerIndex].LinearVelocity()); + } + + @Override + public Quaternion getOrientation(int index) { + return cq(handPoses[index].Orientation()); + } + + @Override + public Vector3f getAngularVelocity(int controllerIndex) { + return cv(handStates[controllerIndex].AngularVelocity()); + } + + @Override + public Quaternion getFinalObserverRotation(int index) { + // Copied from OpenVRInput + + VREnvironment env = hardware.getEnvironment(); + VRViewManagerOculus vrvm = (VRViewManagerOculus) hardware.getEnvironment().getVRViewManager(); + + Object obs = env.getObserver(); + Quaternion tempq = new Quaternion(); // TODO move to class scope? + if (obs instanceof Camera) { + tempq.set(((Camera) obs).getRotation()); + } else { + tempq.set(((Spatial) obs).getWorldRotation()); + } + + return tempq.multLocal(getOrientation(index)); + } + + @Override + public Vector3f getFinalObserverPosition(int index) { + // Copied from OpenVRInput + + VREnvironment env = hardware.getEnvironment(); + VRViewManagerOculus vrvm = (VRViewManagerOculus) hardware.getEnvironment().getVRViewManager(); + + Object obs = env.getObserver(); + Vector3f pos = getPosition(index); + if (obs instanceof Camera) { + ((Camera) obs).getRotation().mult(pos, pos); + return pos.addLocal(((Camera) obs).getLocation()); + } else { + ((Spatial) obs).getWorldRotation().mult(pos, pos); + return pos.addLocal(((Spatial) obs).getWorldTranslation()); + } + } + + @Override + public boolean isInputDeviceTracking(int index) { + int flags = trackingState.HandStatusFlags(index); + return (flags & ovrStatus_PositionTracked) != 0; // TODO do we require orientation as well? + } + + // Input Getters + + @Override + public Vector2f getAxis(int controllerIndex, VRInputType forAxis) { + Vector2f result = getAxisRaw(controllerIndex, forAxis); + return result == null ? null : result.multLocal(axisMultiplier); + } + + @Override + public Vector2f getAxisRaw(int controllerIndex, VRInputType forAxis) { + switch (forAxis) { + case OculusThumbstickAxis: + return cv(inputState.Thumbstick(controllerIndex)); + case OculusTriggerAxis: + return axis(inputState.IndexTrigger(controllerIndex)); + case OculusGripAxis: + return axis(inputState.HandTrigger(controllerIndex)); + default: + return null; + } + } + + @Override + public boolean isButtonDown(int controllerIndex, VRInputType checkButton) { + return isButtonDownForStatus(controllerIndex, checkButton, buttons, touch); + } + + public boolean isButtonDownForStatus(int controllerIndex, VRInputType checkButton, int buttons, int touch) { + int buttonMask = (controllerIndex == ovrHand_Left) ? ovrButton_LMask : ovrButton_RMask; + int touchMask = (controllerIndex == ovrHand_Left) ? + (ovrTouch_LButtonMask + ovrTouch_LPoseMask) : + (ovrTouch_RButtonMask + ovrTouch_RPoseMask); + + switch (checkButton) { + default: + return false; + + case OculusTopButton: // Physical buttons + case OculusBottomButton: + case OculusThumbstickButton: + case OculusMenuButton: + return (buttons & buttonMask & checkButton.getValue()) != 0; + + case OculusTopTouch: // Standard capacitive buttons + case OculusBottomTouch: + case OculusThumbstickTouch: + case OculusThumbrestTouch: + case OculusIndexTouch: + case OculusThumbUp: // Calculated/virtual capacitive buttons + case OculusIndexPointing: + return (touch & touchMask & checkButton.getValue()) != 0; + } + } + + // Since-last-call stuff + + @Override + public void resetInputSinceLastCall() { + lastButtons = 0; + lastTouch = 0; + } + + @Override + public boolean wasButtonPressedSinceLastCall(int controllerIndex, VRInputType checkButton) { + boolean wasPressed = isButtonDownForStatus(controllerIndex, checkButton, lastButtons, lastTouch); + lastButtons = buttons; + lastTouch = touch; + return !wasPressed && isButtonDown(controllerIndex, checkButton); + } + + @Override + public Vector2f getAxisDeltaSinceLastCall(int controllerIndex, VRInputType forAxis) { + int index; + switch (forAxis) { + case OculusTriggerAxis: + index = 0; + break; + case OculusGripAxis: + index = 1; + break; + case OculusThumbstickAxis: + index = 2; + break; + default: + return null; + } + + Vector2f last = lastAxises[controllerIndex][index]; + if (last == null) { + last = lastAxises[controllerIndex][index] = new Vector2f(); + } + + Vector2f current = getAxis(controllerIndex, forAxis); + + // TODO could this lead to accuracy problems? + current.subtractLocal(last); + last.addLocal(current); + + return current; + } + + // Misc + + @Override + public boolean init() { + throw new UnsupportedOperationException("Input initialized at creation time"); + } + + @Override + public void updateConnectedControllers() { + throw new UnsupportedOperationException("Automatically done by LibOVR (I think?)"); + } + + @Override + public float getAxisMultiplier() { + return axisMultiplier; + } + + @Override + public void setAxisMultiplier(float axisMultiplier) { + this.axisMultiplier = axisMultiplier; + } + + @Override + public void triggerHapticPulse(int controllerIndex, float seconds) { + // TODO: How do we time so we can turn the feedback off? + } + + @Override + public boolean isInputFocused() { + return sessionStatus.IsVisible(); // TODO do we need HmdMounted, or is it counted in IsVisible + } + + @Override + public Object getRawControllerState(int index) { + throw new UnsupportedOperationException("Cannot get raw controller state!"); + } + + @Override + public void swapHands() { + // Do nothing. + // TODO although OSVR and OpenVR if it has more than two controllers both do nothing, shouldn't we be + // TODO throwing an exception or something? + } + + @Override + public int getTrackedControllerCount() { + // TODO: Shouldn't we be seeing if the user has the touch controllers first? + return 2; + } + + @Override + public VRTrackedController getTrackedController(int index) { + return controllers[index]; + } + + /** + * The object form representation of a controller. + */ + public class OculusController implements VRTrackedController { + + /** + * The ID of the hand to track + */ + private int hand; + + public OculusController(int hand) { + this.hand = hand; + } + + @Override + public String getControllerName() { + return "Touch"; // TODO + } + + @Override + public String getControllerManufacturer() { + return "Oculus"; // TODO + } + + @Override + public Vector3f getPosition() { + return OculusVRInput.this.getPosition(hand); + } + + @Override + public Quaternion getOrientation() { + return OculusVRInput.this.getOrientation(hand); + } + + @Override + public Matrix4f getPose() { + Matrix4f mat = new Matrix4f(); + mat.setRotationQuaternion(getOrientation()); + mat.setTranslation(getPosition()); + return mat; + } + } +} From 0b37e56bd01cfdcfeff694a2d7d3f0811aa3b7a3 Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Sat, 7 Oct 2017 19:46:39 +1300 Subject: [PATCH 25/27] Add recentering support for Oculus Rift, improve associated Javadoc --- jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java | 4 ++-- jme3-vr/src/main/java/com/jme3/input/vr/VRAPI.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java index 32ba94356..601d19751 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java @@ -329,12 +329,12 @@ public class OculusVR implements VRAPI { @Override public void reset() { - throw new UnsupportedOperationException(); + ovr_RecenterTrackingOrigin(session); } @Override public void getRenderSize(Vector2f store) { - if(!isInitialized()) { + if (!isInitialized()) { throw new IllegalStateException("Cannot call getRenderSize() before initialized!"); } store.x = textureW; diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/VRAPI.java b/jme3-vr/src/main/java/com/jme3/input/vr/VRAPI.java index 4691f4e66..308611d57 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/VRAPI.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/VRAPI.java @@ -86,7 +86,8 @@ public interface VRAPI { public boolean isInitialized(); /** - * Reset the VR system. + * Reset (recenter) the VR system. The current position of the HMD is + * now considered the origin (observer+[0,0,0]). */ public void reset(); From 63dd46b8a36d0515fbb69480662f972ff62ff340 Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Sat, 7 Oct 2017 22:27:16 +1300 Subject: [PATCH 26/27] Switch OculusVR to logger (from system.println), commenting spree --- .../main/java/com/jme3/input/vr/OculusVR.java | 98 +++++++++---------- 1 file changed, 46 insertions(+), 52 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java index 601d19751..5ce192864 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OculusVR.java @@ -8,11 +8,8 @@ package com.jme3.input.vr; import com.jme3.app.VREnvironment; import com.jme3.math.*; import com.jme3.renderer.Camera; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image; -import com.jme3.texture.Texture2D; -import org.lwjgl.BufferUtils; -import org.lwjgl.PointerBuffer; +import com.jme3.texture.*; +import org.lwjgl.*; import org.lwjgl.ovr.*; import java.nio.IntBuffer; @@ -157,43 +154,43 @@ public class OculusVR implements VRAPI { @Override public boolean initialize() { + // Check to make sure the HMD is connected OVRDetectResult detect = OVRDetectResult.calloc(); ovr_Detect(0, detect); boolean connected = detect.IsOculusHMDConnected(); - LOGGER.info("OVRDetectResult.IsOculusHMDConnected = " + connected); - LOGGER.info("OVRDetectResult.IsOculusServiceRunning = " + detect.IsOculusServiceRunning()); + LOGGER.config("OVRDetectResult.IsOculusHMDConnected = " + connected); + LOGGER.config("OVRDetectResult.IsOculusServiceRunning = " + detect.IsOculusServiceRunning()); detect.free(); if (!connected) { + LOGGER.info("Oculus Rift not connected"); return false; } initialized = true; - // step 1 - hmd init - System.out.println("step 1 - hmd init"); + // Set up the HMD OVRLogCallback callback = new OVRLogCallback() { @Override public void invoke(long userData, int level, long message) { - System.out.println("LibOVR [" + userData + "] [" + level + "] " + memASCII(message)); + LOGGER.fine("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"); + LOGGER.severe("LibOVR Init Failed"); + return false; // TODO fix memory leak - destroy() is not called } - System.out.println("OVR SDK " + ovr_GetVersionString()); + LOGGER.config("LibOVR Version " + ovr_GetVersionString()); initParams.free(); - // step 2 - hmd create - System.out.println("step 2 - hmd create"); + // Get access to the HMD + LOGGER.info("Initialize HMD Session"); 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 + LOGGER.severe("Failed to create HMD"); return false; // TODO fix memory leak - destroy() is not called } session = pHmd.get(0); @@ -201,75 +198,75 @@ public class OculusVR implements VRAPI { luid.free(); sessionStatus = OVRSessionStatus.calloc(); - // step 3 - hmdDesc queries - System.out.println("step 3 - hmdDesc queries"); + // Get the information about the HMD + LOGGER.fine("Get HMD properties"); 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"); + LOGGER.warning("No HMD connected"); return false; // TODO fix memory leak - destroy() is not called } resolutionW = hmdDesc.Resolution().w(); resolutionH = hmdDesc.Resolution().h(); - System.out.println("resolution W=" + resolutionW + ", H=" + resolutionH); + + LOGGER.config("HMD Properties: " + + "\t Manufacturer: " + hmdDesc.ManufacturerString() + + "\t Product: " + hmdDesc.ProductNameString() + + "\t Serial: " // + hmdDesc.SerialNumberString() // Hidden for privacy reasons + + "\t Type: " + hmdDesc.Type() + + "\t Resolution (total): " + resolutionW + "," + resolutionH); + if (resolutionW == 0) { - System.out.println("Huh - width=0"); + LOGGER.severe("HMD witdth=0 : aborting"); return false; // TODO fix memory leak - destroy() is not called } - // FOV + // Find the FOV for each eye 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"); + // Get the pose for each eye, and cache it for later. for (int eye = 0; eye < 2; eye++) { + // Create the projection objects projections[eye] = OVRMatrix4f.malloc(); - //1.3 was right handed, now none flag - hmdRelativeEyePoses[eye] = new Matrix4f(); hmdRelativeEyePositions[eye] = new Vector3f(); - } - // step 6 - render desc - System.out.println("step 6 - render desc"); - for (int eye = 0; eye < 2; eye++) { + // Find the eye render information - we use this in the + // view manager for giving LibOVR it's timewarp information. 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()); - + // Get the pose of the eye OVRPosef pose = eyeRenderDesc[eye].HmdToEyePose(); + // Get the position and rotation of the eye vecO2J(pose.Position(), hmdRelativeEyePositions[eye]); + Quaternion rotation = quatO2J(pose.Orientation(), new Quaternion()); + // Put it into a matrix for the get eye pose functions hmdRelativeEyePoses[eye].loadIdentity(); hmdRelativeEyePoses[eye].setTranslation(hmdRelativeEyePositions[eye]); - hmdRelativeEyePoses[eye].setRotationQuaternion(quatO2J(pose.Orientation(), new Quaternion())); + hmdRelativeEyePoses[eye].setRotationQuaternion(rotation); } - // step 7 - recenter - System.out.println("step 7 - recenter"); - ovr_RecenterTrackingOrigin(session); + // Recenter the HMD. The game itself should do this too, but just in case / before they do. + reset(); - // Do this so others relying on our texture size get it correct. + // Do this so others relying on our texture size (the GUI in particular) get it correct. findHMDTextureSize(); - // Set up the tracking system - trackingState = OVRTrackingState.malloc(); + // Allocate the memory for the tracking state - we actually + // set it up later, but Input uses it so calloc it now. + trackingState = OVRTrackingState.calloc(); // Set up the input input = new OculusVRInput(this, session, sessionStatus, trackingState); + // TODO find some way to get in ovrTrackingOrigin_FloorLevel + // throw new UnsupportedOperationException("Not yet implemented!"); return true; } @@ -282,7 +279,6 @@ public class OculusVR implements VRAPI { input.updateControllerStates(); - //get head pose headPose = trackingState.HeadPose().ThePose(); } @@ -329,6 +325,7 @@ public class OculusVR implements VRAPI { @Override public void reset() { + // Reset the coordinate system - where the user's head is now is facing forwards from [0,0,0] ovr_RecenterTrackingOrigin(session); } @@ -452,11 +449,9 @@ public class OculusVR implements VRAPI { OVRSizei leftTextureSize = OVRSizei.malloc(); ovr_GetFovTextureSize(session, ovrEye_Left, fovPorts[ovrEye_Left], pixelScaling, leftTextureSize); - System.out.println("leftTextureSize W=" + leftTextureSize.w() + ", H=" + leftTextureSize.h()); OVRSizei rightTextureSize = OVRSizei.malloc(); ovr_GetFovTextureSize(session, ovrEye_Right, fovPorts[ovrEye_Right], pixelScaling, rightTextureSize); - System.out.println("rightTextureSize W=" + rightTextureSize.w() + ", H=" + rightTextureSize.h()); if (leftTextureSize.w() != rightTextureSize.w()) { throw new IllegalStateException("Texture sizes do not match [horizontal]"); @@ -490,7 +485,6 @@ public class OculusVR implements VRAPI { throw new RuntimeException("Failed to create Swap Texture Set"); } swapChainDesc.free(); - System.out.println("done chain creation"); return textureSetPB.get(); // TODO is this a memory leak? } @@ -533,7 +527,7 @@ public class OculusVR implements VRAPI { ovr_GetTextureSwapChainLength(session, chains[eye], length); int chainLength = length.get(); - System.out.println("chain length=" + chainLength); + LOGGER.fine("HMD Eye #" + eye + " texture chain length: " + chainLength); // Create the frame buffers framebuffers[eye] = new FrameBuffer[chainLength]; From 42fd964a2262d3633c4f52aef292c69eac3dcc8e Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Sat, 7 Oct 2017 22:36:32 +1300 Subject: [PATCH 27/27] Un-break VR mouse support for OpenVR --- jme3-vr/src/main/java/com/jme3/app/VRAppState.java | 6 +++++- jme3-vr/src/main/java/com/jme3/util/VRMouseManager.java | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/jme3-vr/src/main/java/com/jme3/app/VRAppState.java b/jme3-vr/src/main/java/com/jme3/app/VRAppState.java index 6d29dbb24..7dbf7af41 100644 --- a/jme3-vr/src/main/java/com/jme3/app/VRAppState.java +++ b/jme3-vr/src/main/java/com/jme3/app/VRAppState.java @@ -35,6 +35,7 @@ import com.jme3.app.Application; import com.jme3.app.state.AbstractAppState; import com.jme3.app.state.AppStateManager; import com.jme3.input.vr.OculusVR; +import com.jme3.input.vr.OpenVR; import com.jme3.input.vr.VRAPI; import com.jme3.input.vr.VRInputAPI; import com.jme3.math.ColorRGBA; @@ -420,7 +421,10 @@ public class VRAppState extends AbstractAppState { } // use the analog control on the first tracked controller to push around the mouse - // environment.getVRMouseManager().updateAnalogAsMouse(0, null, null, null, tpf); + // FIXME crashes on Rift/Touch (and probably OSVR), as it assumes the presence of the Vive touchpads + if(getVRHardware() instanceof OpenVR) { + environment.getVRMouseManager().updateAnalogAsMouse(0, null, null, null, tpf); + } } @Override diff --git a/jme3-vr/src/main/java/com/jme3/util/VRMouseManager.java b/jme3-vr/src/main/java/com/jme3/util/VRMouseManager.java index 32eaca069..164ea12e6 100644 --- a/jme3-vr/src/main/java/com/jme3/util/VRMouseManager.java +++ b/jme3-vr/src/main/java/com/jme3/util/VRMouseManager.java @@ -182,6 +182,7 @@ public class VRMouseManager { } Vector2f tpDelta; + // TODO option to use Touch joysticks if( thumbstickMode ) { tpDelta = environment.getVRinput().getAxis(inputIndex, VRInputType.ViveTrackpadAxis); } else {