parent
0ecd38892c
commit
2cacf16f69
@ -1,460 +1,460 @@ |
||||
/* |
||||
* 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 java.util.List; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.app.VREnvironment; |
||||
import com.jme3.math.Quaternion; |
||||
import com.jme3.math.Vector2f; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.renderer.Camera; |
||||
import com.jme3.scene.Spatial; |
||||
import com.jme3.system.jopenvr.JOpenVRLibrary; |
||||
import com.jme3.system.jopenvr.OpenVRUtil; |
||||
import com.jme3.system.jopenvr.VRControllerState_t; |
||||
import com.jme3.system.jopenvr.VR_IVRSystem_FnTable; |
||||
import com.jme3.util.VRUtil; |
||||
import com.jme3.util.VRViewManagerOpenVR; |
||||
|
||||
/* |
||||
make helper functions to pull the following easily from raw data (DONE) |
||||
|
||||
trigger: |
||||
Controller#1, Axis#0 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#1 X: 1.0, Y: 0.0 |
||||
Controller#1, Axis#2 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#3 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#4 X: 0.0, Y: 0.0 |
||||
Button press: 8589934592 (when full), touch: 8589934592 |
||||
|
||||
touchpad (upper left): |
||||
Controller#1, Axis#0 X: -0.6059755, Y: 0.2301706 |
||||
Controller#1, Axis#1 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#2 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#3 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#4 X: 0.0, Y: 0.0 |
||||
Button press: 4294967296 (when pressed in), touch: 4294967296 |
||||
|
||||
grip: |
||||
Controller#1, Axis#0 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#1 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#2 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#3 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#4 X: 0.0, Y: 0.0 |
||||
Button press: 4, touch: 4 |
||||
|
||||
thumb: |
||||
Controller#1, Axis#0 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#1 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#2 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#3 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#4 X: 0.0, Y: 0.0 |
||||
Button press: 2, touch: 2 |
||||
|
||||
*/ |
||||
|
||||
/** |
||||
* A class that wraps an <a href="https://github.com/ValveSoftware/openvr/wiki/API-Documentation">OpenVR</a> input.<br> |
||||
* <code>null</code> values will be returned if no valid pose exists, or that input device isn't available |
||||
* user code should check for <code>null</code> values. |
||||
* @author reden - phr00t - https://github.com/phr00t
|
||||
* @author Julien Seinturier - (c) 2016 - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a> |
||||
*/ |
||||
public class OpenVRInput implements VRInputAPI { |
||||
|
||||
private static final Logger logger = Logger.getLogger(OpenVRInput.class.getName()); |
||||
|
||||
private final VRControllerState_t[] cStates = new VRControllerState_t[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; |
||||
|
||||
private final Quaternion[] rotStore = new Quaternion[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; |
||||
|
||||
private final Vector3f[] posStore = new Vector3f[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; |
||||
|
||||
private static final int[] controllerIndex = new int[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; |
||||
|
||||
private int controllerCount = 0; |
||||
|
||||
private final Vector2f tempAxis = new Vector2f(), temp2Axis = new Vector2f(); |
||||
|
||||
private final Vector2f lastCallAxis[] = new Vector2f[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; |
||||
|
||||
private final boolean needsNewVelocity[] = new boolean[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; |
||||
|
||||
private final boolean needsNewAngVelocity[] = new boolean[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; |
||||
|
||||
private final boolean buttonDown[][] = new boolean[JOpenVRLibrary.k_unMaxTrackedDeviceCount][16]; |
||||
|
||||
private float axisMultiplier = 1f; |
||||
|
||||
private final Vector3f tempVel = new Vector3f(); |
||||
|
||||
private final Quaternion tempq = new Quaternion(); |
||||
|
||||
private VREnvironment environment; |
||||
|
||||
private List<VRTrackedController> trackedControllers = null; |
||||
|
||||
/** |
||||
* Create a new <a href="https://github.com/ValveSoftware/openvr/wiki/API-Documentation">OpenVR</a> input attached to the given VR environment. |
||||
* @param environment the VR environment to which the input is attached. |
||||
*/ |
||||
public OpenVRInput(VREnvironment environment){ |
||||
this.environment = environment; |
||||
} |
||||
|
||||
@Override |
||||
public float getAxisMultiplier() { |
||||
return axisMultiplier; |
||||
} |
||||
|
||||
@Override |
||||
public void setAxisMultiplier(float set) { |
||||
axisMultiplier = set; |
||||
} |
||||
|
||||
@Override |
||||
public void swapHands() { |
||||
if( controllerCount != 2 ) return; |
||||
int temp = controllerIndex[0]; |
||||
controllerIndex[0] = controllerIndex[1]; |
||||
controllerIndex[1] = temp; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isButtonDown(int controllerIndex, VRInputType checkButton) { |
||||
VRControllerState_t cs = cStates[OpenVRInput.controllerIndex[controllerIndex]]; |
||||
switch( checkButton ) { |
||||
default: |
||||
return false; |
||||
case ViveGripButton: |
||||
return (cs.ulButtonPressed & 4) != 0; |
||||
case ViveMenuButton: |
||||
return (cs.ulButtonPressed & 2) != 0; |
||||
case ViveTrackpadAxis: |
||||
return (cs.ulButtonPressed & 4294967296l) != 0; |
||||
case ViveTriggerAxis: |
||||
return (cs.ulButtonPressed & 8589934592l) != 0; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public boolean wasButtonPressedSinceLastCall(int controllerIndex, VRInputType checkButton) { |
||||
boolean buttonDownNow = isButtonDown(controllerIndex, checkButton); |
||||
int checkButtonValue = checkButton.getValue(); |
||||
int cIndex = OpenVRInput.controllerIndex[controllerIndex]; |
||||
boolean retval = buttonDownNow == true && buttonDown[cIndex][checkButtonValue] == false; |
||||
buttonDown[cIndex][checkButtonValue] = buttonDownNow; |
||||
return retval; |
||||
} |
||||
|
||||
@Override |
||||
public void resetInputSinceLastCall() { |
||||
for(int i=0;i<lastCallAxis.length;i++) { |
||||
lastCallAxis[i].x = 0f; |
||||
lastCallAxis[i].y = 0f; |
||||
} |
||||
for(int i=0;i<JOpenVRLibrary.k_unMaxTrackedDeviceCount;i++) { |
||||
for(int j=0;j<16;j++) { |
||||
buttonDown[i][j] = false; |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Vector2f getAxisDeltaSinceLastCall(int controllerIndex, VRInputType forAxis) { |
||||
int axisIndex = forAxis.getValue(); |
||||
temp2Axis.set(lastCallAxis[axisIndex]); |
||||
lastCallAxis[axisIndex].set(getAxis(controllerIndex, forAxis)); |
||||
if( (temp2Axis.x != 0f || temp2Axis.y != 0f) && (lastCallAxis[axisIndex].x != 0f || lastCallAxis[axisIndex].y != 0f) ) { |
||||
temp2Axis.subtractLocal(lastCallAxis[axisIndex]); |
||||
} else { |
||||
// move made from rest, don't count as a delta move
|
||||
temp2Axis.x = 0f; |
||||
temp2Axis.y = 0f; |
||||
} |
||||
return temp2Axis; |
||||
} |
||||
|
||||
@Override |
||||
public Vector3f getVelocity(int controllerIndex) { |
||||
int index = OpenVRInput.controllerIndex[controllerIndex]; |
||||
if( needsNewVelocity[index] ) { |
||||
OpenVR.hmdTrackedDevicePoses[index].readField("vVelocity"); |
||||
needsNewVelocity[index] = false; |
||||
} |
||||
tempVel.x = OpenVR.hmdTrackedDevicePoses[index].vVelocity.v[0]; |
||||
tempVel.y = OpenVR.hmdTrackedDevicePoses[index].vVelocity.v[1]; |
||||
tempVel.z = OpenVR.hmdTrackedDevicePoses[index].vVelocity.v[2]; |
||||
return tempVel; |
||||
} |
||||
|
||||
@Override |
||||
public Vector3f getAngularVelocity(int controllerIndex) { |
||||
int index = OpenVRInput.controllerIndex[controllerIndex]; |
||||
if( needsNewAngVelocity[index] ) { |
||||
OpenVR.hmdTrackedDevicePoses[index].readField("vAngularVelocity"); |
||||
needsNewAngVelocity[index] = false; |
||||
} |
||||
tempVel.x = OpenVR.hmdTrackedDevicePoses[index].vAngularVelocity.v[0]; |
||||
tempVel.y = OpenVR.hmdTrackedDevicePoses[index].vAngularVelocity.v[1]; |
||||
tempVel.z = OpenVR.hmdTrackedDevicePoses[index].vAngularVelocity.v[2]; |
||||
return tempVel; |
||||
} |
||||
|
||||
@Override |
||||
public Vector2f getAxisRaw(int controllerIndex, VRInputType forAxis) { |
||||
VRControllerState_t cs = cStates[OpenVRInput.controllerIndex[controllerIndex]]; |
||||
switch( forAxis ) { |
||||
default: |
||||
return null; |
||||
case ViveTriggerAxis: |
||||
tempAxis.x = cs.rAxis[1].x; |
||||
tempAxis.y = tempAxis.x; |
||||
break; |
||||
case ViveTrackpadAxis: |
||||
tempAxis.x = cs.rAxis[0].x; |
||||
tempAxis.y = cs.rAxis[0].y; |
||||
break; |
||||
} |
||||
return tempAxis; |
||||
} |
||||
|
||||
@Override |
||||
public Vector2f getAxis(int controllerIndex, VRInputType forAxis) { |
||||
VRControllerState_t cs = cStates[OpenVRInput.controllerIndex[controllerIndex]]; |
||||
switch( forAxis ) { |
||||
default: |
||||
return null; |
||||
case ViveTriggerAxis: |
||||
tempAxis.x = cs.rAxis[1].x; |
||||
tempAxis.y = tempAxis.x; |
||||
break; |
||||
case ViveTrackpadAxis: |
||||
tempAxis.x = cs.rAxis[0].x; |
||||
tempAxis.y = cs.rAxis[0].y; |
||||
break; |
||||
} |
||||
tempAxis.x *= axisMultiplier; |
||||
tempAxis.y *= axisMultiplier; |
||||
return tempAxis; |
||||
} |
||||
|
||||
@Override |
||||
public boolean init() { |
||||
|
||||
logger.config("Initialize OpenVR input."); |
||||
|
||||
for(int i=0;i<JOpenVRLibrary.k_unMaxTrackedDeviceCount;i++) { |
||||
rotStore[i] = new Quaternion(); |
||||
posStore[i] = new Vector3f(); |
||||
cStates[i] = new VRControllerState_t(); |
||||
cStates[i].setAutoSynch(false); |
||||
cStates[i].setAutoRead(false); |
||||
cStates[i].setAutoWrite(false); |
||||
lastCallAxis[i] = new Vector2f(); |
||||
needsNewVelocity[i] = true; |
||||
needsNewAngVelocity[i] = true; |
||||
logger.config(" Input "+(i+1)+"/"+JOpenVRLibrary.k_unMaxTrackedDeviceCount+" binded."); |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public VRTrackedController getTrackedController(int index){ |
||||
if (trackedControllers != null){ |
||||
if ((trackedControllers.size() > 0) && (index < trackedControllers.size())){ |
||||
return trackedControllers.get(index); |
||||
} |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public int getTrackedControllerCount() { |
||||
return controllerCount; |
||||
} |
||||
|
||||
@Override |
||||
public VRControllerState_t getRawControllerState(int index) { |
||||
if( isInputDeviceTracking(index) == false ) return null; |
||||
return cStates[controllerIndex[index]]; |
||||
} |
||||
|
||||
//public Matrix4f getPoseForInputDevice(int index) {
|
||||
// if( isInputDeviceTracking(index) == false ) return null;
|
||||
// return OpenVR.poseMatrices[controllerIndex[index]];
|
||||
//}
|
||||
|
||||
@Override |
||||
public boolean isInputFocused() { |
||||
|
||||
if (environment != null){ |
||||
return ((VR_IVRSystem_FnTable)environment.getVRHardware().getVRSystem()).IsInputFocusCapturedByAnotherProcess.apply() == 0; |
||||
} else { |
||||
throw new IllegalStateException("VR input is not attached to a VR environment."); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public boolean isInputDeviceTracking(int index) { |
||||
if( index < 0 || index >= controllerCount ){ |
||||
return false; |
||||
} |
||||
|
||||
return OpenVR.hmdTrackedDevicePoses[controllerIndex[index]].bPoseIsValid != 0; |
||||
} |
||||
|
||||
@Override |
||||
public Quaternion getOrientation(int index) { |
||||
if( isInputDeviceTracking(index) == false ){ |
||||
return null; |
||||
} |
||||
index = controllerIndex[index]; |
||||
VRUtil.convertMatrix4toQuat(OpenVR.poseMatrices[index], rotStore[index]); |
||||
return rotStore[index]; |
||||
} |
||||
|
||||
@Override |
||||
public Vector3f getPosition(int index) { |
||||
if( isInputDeviceTracking(index) == false ){ |
||||
return null; |
||||
} |
||||
|
||||
// the hmdPose comes in rotated funny, fix that here
|
||||
index = controllerIndex[index]; |
||||
OpenVR.poseMatrices[index].toTranslationVector(posStore[index]); |
||||
posStore[index].x = -posStore[index].x; |
||||
posStore[index].z = -posStore[index].z; |
||||
return posStore[index]; |
||||
} |
||||
|
||||
@Override |
||||
public Quaternion getFinalObserverRotation(int index) { |
||||
|
||||
if (environment != null){ |
||||
VRViewManagerOpenVR vrvm = (VRViewManagerOpenVR)environment.getVRViewManager(); |
||||
|
||||
if (vrvm != null){ |
||||
if(isInputDeviceTracking(index) == false ){ |
||||
return null; |
||||
} |
||||
|
||||
Object obs = environment.getObserver(); |
||||
if( obs instanceof Camera ) { |
||||
tempq.set(((Camera)obs).getRotation()); |
||||
} else { |
||||
tempq.set(((Spatial)obs).getWorldRotation()); |
||||
} |
||||
|
||||
return tempq.multLocal(getOrientation(index)); |
||||
} else { |
||||
throw new IllegalStateException("VR environment has no valid view manager."); |
||||
} |
||||
|
||||
|
||||
} else { |
||||
throw new IllegalStateException("VR input is not attached to a VR environment."); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Vector3f getFinalObserverPosition(int index) { |
||||
|
||||
if (environment != null){ |
||||
VRViewManagerOpenVR vrvm = (VRViewManagerOpenVR)environment.getVRViewManager(); |
||||
|
||||
if (vrvm != null){ |
||||
if(isInputDeviceTracking(index) == false ){ |
||||
return null; |
||||
} |
||||
Object obs = environment.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()); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("VR environment has no valid view manager."); |
||||
} |
||||
|
||||
} else { |
||||
throw new IllegalStateException("VR input is not attached to a VR environment."); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void triggerHapticPulse(int controllerIndex, float seconds) { |
||||
if( environment.isInVR() == false || isInputDeviceTracking(controllerIndex) == false ){ |
||||
return; |
||||
} |
||||
|
||||
// apparently only axis ID of 0 works
|
||||
((VR_IVRSystem_FnTable)environment.getVRHardware().getVRSystem()).TriggerHapticPulse.apply(OpenVRInput.controllerIndex[controllerIndex], |
||||
0, (short)Math.round(3f * seconds / 1e-3f)); |
||||
} |
||||
|
||||
@Override |
||||
public void updateConnectedControllers() { |
||||
logger.config("Updating connected controllers."); |
||||
|
||||
if (environment != null){ |
||||
controllerCount = 0; |
||||
for(int i=0;i<JOpenVRLibrary.k_unMaxTrackedDeviceCount;i++) { |
||||
int classCallback = ((OpenVR)environment.getVRHardware()).getVRSystem().GetTrackedDeviceClass.apply(i); |
||||
if( classCallback == JOpenVRLibrary.ETrackedDeviceClass.ETrackedDeviceClass_TrackedDeviceClass_Controller || classCallback == JOpenVRLibrary.ETrackedDeviceClass.ETrackedDeviceClass_TrackedDeviceClass_GenericTracker) { |
||||
|
||||
String controllerName = "Unknown"; |
||||
String manufacturerName = "Unknown"; |
||||
try { |
||||
controllerName = OpenVRUtil.getTrackedDeviceStringProperty(((OpenVR)environment.getVRHardware()).getVRSystem(), i, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_TrackingSystemName_String); |
||||
manufacturerName = OpenVRUtil.getTrackedDeviceStringProperty(((OpenVR)environment.getVRHardware()).getVRSystem(), i, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ManufacturerName_String); |
||||
} catch (Exception e) { |
||||
logger.log(Level.WARNING, e.getMessage(), e); |
||||
} |
||||
|
||||
controllerIndex[controllerCount] = i; |
||||
|
||||
// Send an Haptic pulse to the controller
|
||||
triggerHapticPulse(controllerCount, 1.0f); |
||||
|
||||
controllerCount++; |
||||
logger.config(" Tracked controller "+(i+1)+"/"+JOpenVRLibrary.k_unMaxTrackedDeviceCount+" "+controllerName+" ("+manufacturerName+") attached."); |
||||
} else { |
||||
logger.config(" Controller "+(i+1)+"/"+JOpenVRLibrary.k_unMaxTrackedDeviceCount+" ignored."); |
||||
} |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("VR input is not attached to a VR environment."); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void updateControllerStates() { |
||||
|
||||
if (environment != null){ |
||||
for(int i=0;i<controllerCount;i++) { |
||||
int index = controllerIndex[i]; |
||||
((OpenVR)environment.getVRHardware()).getVRSystem().GetControllerState.apply(index, cStates[index], 64); |
||||
cStates[index].readField("ulButtonPressed"); |
||||
cStates[index].readField("rAxis"); |
||||
needsNewVelocity[index] = true; |
||||
needsNewAngVelocity[index] = true; |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("VR input is not attached to a VR environment."); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
/* |
||||
* 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 java.util.List; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.app.VREnvironment; |
||||
import com.jme3.math.Quaternion; |
||||
import com.jme3.math.Vector2f; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.renderer.Camera; |
||||
import com.jme3.scene.Spatial; |
||||
import com.jme3.system.jopenvr.JOpenVRLibrary; |
||||
import com.jme3.system.jopenvr.OpenVRUtil; |
||||
import com.jme3.system.jopenvr.VRControllerState_t; |
||||
import com.jme3.system.jopenvr.VR_IVRSystem_FnTable; |
||||
import com.jme3.util.VRUtil; |
||||
import com.jme3.util.VRViewManagerOpenVR; |
||||
|
||||
/* |
||||
make helper functions to pull the following easily from raw data (DONE) |
||||
|
||||
trigger: |
||||
Controller#1, Axis#0 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#1 X: 1.0, Y: 0.0 |
||||
Controller#1, Axis#2 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#3 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#4 X: 0.0, Y: 0.0 |
||||
Button press: 8589934592 (when full), touch: 8589934592 |
||||
|
||||
touchpad (upper left): |
||||
Controller#1, Axis#0 X: -0.6059755, Y: 0.2301706 |
||||
Controller#1, Axis#1 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#2 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#3 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#4 X: 0.0, Y: 0.0 |
||||
Button press: 4294967296 (when pressed in), touch: 4294967296 |
||||
|
||||
grip: |
||||
Controller#1, Axis#0 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#1 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#2 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#3 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#4 X: 0.0, Y: 0.0 |
||||
Button press: 4, touch: 4 |
||||
|
||||
thumb: |
||||
Controller#1, Axis#0 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#1 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#2 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#3 X: 0.0, Y: 0.0 |
||||
Controller#1, Axis#4 X: 0.0, Y: 0.0 |
||||
Button press: 2, touch: 2 |
||||
|
||||
*/ |
||||
|
||||
/** |
||||
* A class that wraps an <a href="https://github.com/ValveSoftware/openvr/wiki/API-Documentation">OpenVR</a> input.<br> |
||||
* <code>null</code> values will be returned if no valid pose exists, or that input device isn't available |
||||
* user code should check for <code>null</code> values. |
||||
* @author reden - phr00t - https://github.com/phr00t
|
||||
* @author Julien Seinturier - (c) 2016 - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a> |
||||
*/ |
||||
public class OpenVRInput implements VRInputAPI { |
||||
|
||||
private static final Logger logger = Logger.getLogger(OpenVRInput.class.getName()); |
||||
|
||||
private final VRControllerState_t[] cStates = new VRControllerState_t[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; |
||||
|
||||
private final Quaternion[] rotStore = new Quaternion[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; |
||||
|
||||
private final Vector3f[] posStore = new Vector3f[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; |
||||
|
||||
private static final int[] controllerIndex = new int[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; |
||||
|
||||
private int controllerCount = 0; |
||||
|
||||
private final Vector2f tempAxis = new Vector2f(), temp2Axis = new Vector2f(); |
||||
|
||||
private final Vector2f lastCallAxis[] = new Vector2f[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; |
||||
|
||||
private final boolean needsNewVelocity[] = new boolean[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; |
||||
|
||||
private final boolean needsNewAngVelocity[] = new boolean[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; |
||||
|
||||
private final boolean buttonDown[][] = new boolean[JOpenVRLibrary.k_unMaxTrackedDeviceCount][16]; |
||||
|
||||
private float axisMultiplier = 1f; |
||||
|
||||
private final Vector3f tempVel = new Vector3f(); |
||||
|
||||
private final Quaternion tempq = new Quaternion(); |
||||
|
||||
private VREnvironment environment; |
||||
|
||||
private List<VRTrackedController> trackedControllers = null; |
||||
|
||||
/** |
||||
* Create a new <a href="https://github.com/ValveSoftware/openvr/wiki/API-Documentation">OpenVR</a> input attached to the given VR environment. |
||||
* @param environment the VR environment to which the input is attached. |
||||
*/ |
||||
public OpenVRInput(VREnvironment environment){ |
||||
this.environment = environment; |
||||
} |
||||
|
||||
@Override |
||||
public float getAxisMultiplier() { |
||||
return axisMultiplier; |
||||
} |
||||
|
||||
@Override |
||||
public void setAxisMultiplier(float set) { |
||||
axisMultiplier = set; |
||||
} |
||||
|
||||
@Override |
||||
public void swapHands() { |
||||
if( controllerCount != 2 ) return; |
||||
int temp = controllerIndex[0]; |
||||
controllerIndex[0] = controllerIndex[1]; |
||||
controllerIndex[1] = temp; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isButtonDown(int controllerIndex, VRInputType checkButton) { |
||||
VRControllerState_t cs = cStates[OpenVRInput.controllerIndex[controllerIndex]]; |
||||
switch( checkButton ) { |
||||
default: |
||||
return false; |
||||
case ViveGripButton: |
||||
return (cs.ulButtonPressed & 4) != 0; |
||||
case ViveMenuButton: |
||||
return (cs.ulButtonPressed & 2) != 0; |
||||
case ViveTrackpadAxis: |
||||
return (cs.ulButtonPressed & 4294967296l) != 0; |
||||
case ViveTriggerAxis: |
||||
return (cs.ulButtonPressed & 8589934592l) != 0; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public boolean wasButtonPressedSinceLastCall(int controllerIndex, VRInputType checkButton) { |
||||
boolean buttonDownNow = isButtonDown(controllerIndex, checkButton); |
||||
int checkButtonValue = checkButton.getValue(); |
||||
int cIndex = OpenVRInput.controllerIndex[controllerIndex]; |
||||
boolean retval = buttonDownNow == true && buttonDown[cIndex][checkButtonValue] == false; |
||||
buttonDown[cIndex][checkButtonValue] = buttonDownNow; |
||||
return retval; |
||||
} |
||||
|
||||
@Override |
||||
public void resetInputSinceLastCall() { |
||||
for(int i=0;i<lastCallAxis.length;i++) { |
||||
lastCallAxis[i].x = 0f; |
||||
lastCallAxis[i].y = 0f; |
||||
} |
||||
for(int i=0;i<JOpenVRLibrary.k_unMaxTrackedDeviceCount;i++) { |
||||
for(int j=0;j<16;j++) { |
||||
buttonDown[i][j] = false; |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Vector2f getAxisDeltaSinceLastCall(int controllerIndex, VRInputType forAxis) { |
||||
int axisIndex = forAxis.getValue(); |
||||
temp2Axis.set(lastCallAxis[axisIndex]); |
||||
lastCallAxis[axisIndex].set(getAxis(controllerIndex, forAxis)); |
||||
if( (temp2Axis.x != 0f || temp2Axis.y != 0f) && (lastCallAxis[axisIndex].x != 0f || lastCallAxis[axisIndex].y != 0f) ) { |
||||
temp2Axis.subtractLocal(lastCallAxis[axisIndex]); |
||||
} else { |
||||
// move made from rest, don't count as a delta move
|
||||
temp2Axis.x = 0f; |
||||
temp2Axis.y = 0f; |
||||
} |
||||
return temp2Axis; |
||||
} |
||||
|
||||
@Override |
||||
public Vector3f getVelocity(int controllerIndex) { |
||||
int index = OpenVRInput.controllerIndex[controllerIndex]; |
||||
if( needsNewVelocity[index] ) { |
||||
OpenVR.hmdTrackedDevicePoses[index].readField("vVelocity"); |
||||
needsNewVelocity[index] = false; |
||||
} |
||||
tempVel.x = OpenVR.hmdTrackedDevicePoses[index].vVelocity.v[0]; |
||||
tempVel.y = OpenVR.hmdTrackedDevicePoses[index].vVelocity.v[1]; |
||||
tempVel.z = OpenVR.hmdTrackedDevicePoses[index].vVelocity.v[2]; |
||||
return tempVel; |
||||
} |
||||
|
||||
@Override |
||||
public Vector3f getAngularVelocity(int controllerIndex) { |
||||
int index = OpenVRInput.controllerIndex[controllerIndex]; |
||||
if( needsNewAngVelocity[index] ) { |
||||
OpenVR.hmdTrackedDevicePoses[index].readField("vAngularVelocity"); |
||||
needsNewAngVelocity[index] = false; |
||||
} |
||||
tempVel.x = OpenVR.hmdTrackedDevicePoses[index].vAngularVelocity.v[0]; |
||||
tempVel.y = OpenVR.hmdTrackedDevicePoses[index].vAngularVelocity.v[1]; |
||||
tempVel.z = OpenVR.hmdTrackedDevicePoses[index].vAngularVelocity.v[2]; |
||||
return tempVel; |
||||
} |
||||
|
||||
@Override |
||||
public Vector2f getAxisRaw(int controllerIndex, VRInputType forAxis) { |
||||
VRControllerState_t cs = cStates[OpenVRInput.controllerIndex[controllerIndex]]; |
||||
switch( forAxis ) { |
||||
default: |
||||
return null; |
||||
case ViveTriggerAxis: |
||||
tempAxis.x = cs.rAxis[1].x; |
||||
tempAxis.y = tempAxis.x; |
||||
break; |
||||
case ViveTrackpadAxis: |
||||
tempAxis.x = cs.rAxis[0].x; |
||||
tempAxis.y = cs.rAxis[0].y; |
||||
break; |
||||
} |
||||
return tempAxis; |
||||
} |
||||
|
||||
@Override |
||||
public Vector2f getAxis(int controllerIndex, VRInputType forAxis) { |
||||
VRControllerState_t cs = cStates[OpenVRInput.controllerIndex[controllerIndex]]; |
||||
switch( forAxis ) { |
||||
default: |
||||
return null; |
||||
case ViveTriggerAxis: |
||||
tempAxis.x = cs.rAxis[1].x; |
||||
tempAxis.y = tempAxis.x; |
||||
break; |
||||
case ViveTrackpadAxis: |
||||
tempAxis.x = cs.rAxis[0].x; |
||||
tempAxis.y = cs.rAxis[0].y; |
||||
break; |
||||
} |
||||
tempAxis.x *= axisMultiplier; |
||||
tempAxis.y *= axisMultiplier; |
||||
return tempAxis; |
||||
} |
||||
|
||||
@Override |
||||
public boolean init() { |
||||
|
||||
logger.config("Initialize OpenVR input."); |
||||
|
||||
for(int i=0;i<JOpenVRLibrary.k_unMaxTrackedDeviceCount;i++) { |
||||
rotStore[i] = new Quaternion(); |
||||
posStore[i] = new Vector3f(); |
||||
cStates[i] = new VRControllerState_t(); |
||||
cStates[i].setAutoSynch(false); |
||||
cStates[i].setAutoRead(false); |
||||
cStates[i].setAutoWrite(false); |
||||
lastCallAxis[i] = new Vector2f(); |
||||
needsNewVelocity[i] = true; |
||||
needsNewAngVelocity[i] = true; |
||||
logger.config(" Input "+(i+1)+"/"+JOpenVRLibrary.k_unMaxTrackedDeviceCount+" binded."); |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public VRTrackedController getTrackedController(int index){ |
||||
if (trackedControllers != null){ |
||||
if ((trackedControllers.size() > 0) && (index < trackedControllers.size())){ |
||||
return trackedControllers.get(index); |
||||
} |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public int getTrackedControllerCount() { |
||||
return controllerCount; |
||||
} |
||||
|
||||
@Override |
||||
public VRControllerState_t getRawControllerState(int index) { |
||||
if( isInputDeviceTracking(index) == false ) return null; |
||||
return cStates[controllerIndex[index]]; |
||||
} |
||||
|
||||
//public Matrix4f getPoseForInputDevice(int index) {
|
||||
// if( isInputDeviceTracking(index) == false ) return null;
|
||||
// return OpenVR.poseMatrices[controllerIndex[index]];
|
||||
//}
|
||||
|
||||
@Override |
||||
public boolean isInputFocused() { |
||||
|
||||
if (environment != null){ |
||||
return ((VR_IVRSystem_FnTable)environment.getVRHardware().getVRSystem()).IsInputFocusCapturedByAnotherProcess.apply() == 0; |
||||
} else { |
||||
throw new IllegalStateException("VR input is not attached to a VR environment."); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public boolean isInputDeviceTracking(int index) { |
||||
if( index < 0 || index >= controllerCount ){ |
||||
return false; |
||||
} |
||||
|
||||
return OpenVR.hmdTrackedDevicePoses[controllerIndex[index]].bPoseIsValid != 0; |
||||
} |
||||
|
||||
@Override |
||||
public Quaternion getOrientation(int index) { |
||||
if( isInputDeviceTracking(index) == false ){ |
||||
return null; |
||||
} |
||||
index = controllerIndex[index]; |
||||
VRUtil.convertMatrix4toQuat(OpenVR.poseMatrices[index], rotStore[index]); |
||||
return rotStore[index]; |
||||
} |
||||
|
||||
@Override |
||||
public Vector3f getPosition(int index) { |
||||
if( isInputDeviceTracking(index) == false ){ |
||||
return null; |
||||
} |
||||
|
||||
// the hmdPose comes in rotated funny, fix that here
|
||||
index = controllerIndex[index]; |
||||
OpenVR.poseMatrices[index].toTranslationVector(posStore[index]); |
||||
posStore[index].x = -posStore[index].x; |
||||
posStore[index].z = -posStore[index].z; |
||||
return posStore[index]; |
||||
} |
||||
|
||||
@Override |
||||
public Quaternion getFinalObserverRotation(int index) { |
||||
|
||||
if (environment != null){ |
||||
VRViewManagerOpenVR vrvm = (VRViewManagerOpenVR)environment.getVRViewManager(); |
||||
|
||||
if (vrvm != null){ |
||||
if(isInputDeviceTracking(index) == false ){ |
||||
return null; |
||||
} |
||||
|
||||
Object obs = environment.getObserver(); |
||||
if( obs instanceof Camera ) { |
||||
tempq.set(((Camera)obs).getRotation()); |
||||
} else { |
||||
tempq.set(((Spatial)obs).getWorldRotation()); |
||||
} |
||||
|
||||
return tempq.multLocal(getOrientation(index)); |
||||
} else { |
||||
throw new IllegalStateException("VR environment has no valid view manager."); |
||||
} |
||||
|
||||
|
||||
} else { |
||||
throw new IllegalStateException("VR input is not attached to a VR environment."); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Vector3f getFinalObserverPosition(int index) { |
||||
|
||||
if (environment != null){ |
||||
VRViewManagerOpenVR vrvm = (VRViewManagerOpenVR)environment.getVRViewManager(); |
||||
|
||||
if (vrvm != null){ |
||||
if(isInputDeviceTracking(index) == false ){ |
||||
return null; |
||||
} |
||||
Object obs = environment.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()); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("VR environment has no valid view manager."); |
||||
} |
||||
|
||||
} else { |
||||
throw new IllegalStateException("VR input is not attached to a VR environment."); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void triggerHapticPulse(int controllerIndex, float seconds) { |
||||
if( environment.isInVR() == false || isInputDeviceTracking(controllerIndex) == false ){ |
||||
return; |
||||
} |
||||
|
||||
// apparently only axis ID of 0 works
|
||||
((VR_IVRSystem_FnTable)environment.getVRHardware().getVRSystem()).TriggerHapticPulse.apply(OpenVRInput.controllerIndex[controllerIndex], |
||||
0, (short)Math.round(3f * seconds / 1e-3f)); |
||||
} |
||||
|
||||
@Override |
||||
public void updateConnectedControllers() { |
||||
logger.config("Updating connected controllers."); |
||||
|
||||
if (environment != null){ |
||||
controllerCount = 0; |
||||
for(int i=0;i<JOpenVRLibrary.k_unMaxTrackedDeviceCount;i++) { |
||||
int classCallback = ((OpenVR)environment.getVRHardware()).getVRSystem().GetTrackedDeviceClass.apply(i); |
||||
if( classCallback == JOpenVRLibrary.ETrackedDeviceClass.ETrackedDeviceClass_TrackedDeviceClass_Controller || classCallback == JOpenVRLibrary.ETrackedDeviceClass.ETrackedDeviceClass_TrackedDeviceClass_GenericTracker) { |
||||
|
||||
String controllerName = "Unknown"; |
||||
String manufacturerName = "Unknown"; |
||||
try { |
||||
controllerName = OpenVRUtil.getTrackedDeviceStringProperty(((OpenVR)environment.getVRHardware()).getVRSystem(), i, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_TrackingSystemName_String); |
||||
manufacturerName = OpenVRUtil.getTrackedDeviceStringProperty(((OpenVR)environment.getVRHardware()).getVRSystem(), i, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ManufacturerName_String); |
||||
} catch (Exception e) { |
||||
logger.log(Level.WARNING, e.getMessage(), e); |
||||
} |
||||
|
||||
controllerIndex[controllerCount] = i; |
||||
|
||||
// Send an Haptic pulse to the controller
|
||||
triggerHapticPulse(controllerCount, 1.0f); |
||||
|
||||
controllerCount++; |
||||
logger.config(" Tracked controller "+(i+1)+"/"+JOpenVRLibrary.k_unMaxTrackedDeviceCount+" "+controllerName+" ("+manufacturerName+") attached."); |
||||
} else { |
||||
logger.config(" Controller "+(i+1)+"/"+JOpenVRLibrary.k_unMaxTrackedDeviceCount+" ignored."); |
||||
} |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("VR input is not attached to a VR environment."); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void updateControllerStates() { |
||||
|
||||
if (environment != null){ |
||||
for(int i=0;i<controllerCount;i++) { |
||||
int index = controllerIndex[i]; |
||||
((OpenVR)environment.getVRHardware()).getVRSystem().GetControllerState.apply(index, cStates[index], 64); |
||||
cStates[index].readField("ulButtonPressed"); |
||||
cStates[index].readField("rAxis"); |
||||
needsNewVelocity[index] = true; |
||||
needsNewAngVelocity[index] = true; |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("VR input is not attached to a VR environment."); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
Loading…
Reference in new issue