#![cfg(target_os="android")]
#![cfg(feature = "oculusvr")]
use {VRGamepad, VRGamepadButton, VRGamepadData, VRGamepadHand, VRGamepadState};
use ovr_mobile_sys as ovr;
use ovr_mobile_sys::ovrButton::*;
use ovr_mobile_sys::ovrControllerCapabilties::*;
use ovr_mobile_sys::ovrControllerType::*;
use std::cell::{Cell, RefCell};
use std::mem;
use std::ptr;
use std::sync::Arc;
use super::display::{ovr_quat_to_array, ovr_vec3_to_array};
use rust_webvr_api::utils;
pub type OculusVRGamepadPtr = Arc<RefCell<OculusVRGamepad>>;
pub struct OculusVRGamepad {
ovr: *mut ovr::ovrMobile,
ovr_id: ovr::ovrDeviceID,
ovr_type: ovr::ovrControllerType,
connected: bool,
capabilities: InputCapabilities,
gamepad_id: u32,
display_id: u32,
predicted_display_time: Cell<f64>,
}
unsafe impl Send for OculusVRGamepad {}
unsafe impl Sync for OculusVRGamepad {}
impl OculusVRGamepad {
pub fn new(ovr: *mut ovr::ovrMobile,
ovr_id: ovr::ovrDeviceID,
ovr_type: ovr::ovrControllerType,
display_id: u32)
-> Arc<RefCell<OculusVRGamepad>>
{
let capabilities = InputCapabilities::from_ovr(ovr, ovr_type, ovr_id);
let gamepad = Self {
ovr: ovr,
ovr_id: ovr_id,
ovr_type: ovr_type,
connected: true,
capabilities: capabilities.unwrap_or_default(),
gamepad_id: utils::new_id(),
display_id: display_id,
predicted_display_time: Cell::new(0.0),
};
Arc::new(RefCell::new(gamepad))
}
pub fn refresh_available_gamepads(ovr: *mut ovr::ovrMobile,
display_id: u32,
out: &mut Vec<OculusVRGamepadPtr>) {
let mut index = 0;
for gamepad in out.iter() {
gamepad.borrow_mut().connected = false;
}
loop {
let mut caps: ovr::ovrInputCapabilityHeader = unsafe { mem::uninitialized() };
if unsafe { ovr::vrapi_EnumerateInputDevices(ovr, index, &mut caps) } < 0 {
break;
}
index += 1;
if caps.Type != ovrControllerType_TrackedRemote && caps.Type != ovrControllerType_Headset {
continue;
}
if let Some(gamepad) = out.iter().find(|g| g.borrow().ovr_type == caps.Type).as_ref() {
let mut gamepad = gamepad.borrow_mut();
gamepad.ovr = ovr;
gamepad.ovr_id = caps.DeviceID;
gamepad.connected = true;
gamepad.update_capabilities();
continue;
}
let gamepad = OculusVRGamepad::new(ovr, caps.DeviceID, caps.Type, display_id);
out.push(gamepad);
}
}
pub fn update_capabilities(&mut self) {
if let Ok(capabilities) = InputCapabilities::from_ovr(self.ovr, self.ovr_type, self.ovr_id) {
self.capabilities = capabilities;
}
}
pub fn on_exit_vrmode(&mut self) {
self.connected = false;
self.ovr = ptr::null_mut();
}
fn fetch_axes(&self, touching: bool, pos: &ovr::ovrVector2f, out: &mut VRGamepadState) {
let x = pos.x / self.capabilities.trackpad_max_x as f32;
let y = pos.y / self.capabilities.trackpad_max_y as f32;
out.axes = if touching {
[x as f64 * 2.0 - 1.0,
y as f64 * 2.0 - 1.0].to_vec()
} else {
[0.0, 0.0].to_vec()
};
}
fn fetch_remote_controller_state(&self, out: &mut VRGamepadState) {
let mut state: ovr::ovrInputStateTrackedRemote = unsafe { mem::zeroed() };
state.Header.ControllerType = ovrControllerType_TrackedRemote;
unsafe {
ovr::vrapi_GetCurrentInputState(self.ovr, self.ovr_id, &mut state.Header);
}
let touching_trackpad = state.TrackpadStatus > 0;
self.fetch_axes(touching_trackpad, &state.TrackpadPosition, out);
out.buttons.push(VRGamepadButton::new(touching_trackpad));
out.buttons.push(VRGamepadButton::new(state.Buttons & (ovrButton_A as u32) > 0));
}
fn fetch_headset_controller_state(&self, out: &mut VRGamepadState) {
let mut state: ovr::ovrInputStateHeadset = unsafe { mem::zeroed() };
state.Header.ControllerType = ovrControllerType_Headset;
unsafe {
ovr::vrapi_GetCurrentInputState(self.ovr, self.ovr_id, &mut state.Header);
}
let touching_trackpad = state.TrackpadStatus > 0;
self.fetch_axes(touching_trackpad, &state.TrackpadPosition, out);
out.buttons.push(VRGamepadButton::new(touching_trackpad));
out.buttons.push(VRGamepadButton::new(state.Buttons & (ovrButton_A as u32) > 0));
}
fn fetch_tracking_state(&self, out: &mut VRGamepadState) {
let mut tracking: ovr::ovrTracking = unsafe { mem::uninitialized() };
let status = unsafe {
ovr::vrapi_GetInputTrackingState(self.ovr,
self.ovr_id,
self.predicted_display_time.get(),
&mut tracking)
};
if status != ovr::ovrSuccessResult::ovrSuccess as i32 {
out.connected = false;
return;
}
if self.capabilities.controller_capabilities & (ovrControllerCaps_HasOrientationTracking as u32) > 0 {
out.pose.orientation = Some(ovr_quat_to_array(&tracking.HeadPose.Pose.Orientation));
}
if self.capabilities.controller_capabilities & (ovrControllerCaps_HasPositionTracking as u32) > 0 {
out.pose.position = Some(ovr_vec3_to_array(&tracking.HeadPose.Pose.Position));
}
}
pub fn set_predicted_display_time(&self, time: f64) {
self.predicted_display_time.set(time);
}
}
impl VRGamepad for OculusVRGamepad {
fn id(&self) -> u32 {
self.gamepad_id
}
fn data(&self) -> VRGamepadData {
let name = if self.ovr_type == ovrControllerType_TrackedRemote {
"Gear VR Remote Controller"
} else {
"Gear VR Headset Controller"
};
let hand = if self.capabilities.controller_capabilities & (ovrControllerCaps_RightHand as u32) > 0 {
VRGamepadHand::Right
} else if self.capabilities.controller_capabilities & (ovrControllerCaps_LeftHand as u32) > 0 {
VRGamepadHand::Left
} else {
VRGamepadHand::Unknown
};
VRGamepadData {
display_id: self.display_id,
name: name.into(),
hand: hand,
}
}
fn state(&self) -> VRGamepadState {
let mut out = VRGamepadState::default();
out.gamepad_id = self.gamepad_id;
out.connected = self.connected && !self.ovr.is_null();
if out.connected {
if self.ovr_type == ovrControllerType_TrackedRemote {
self.fetch_remote_controller_state(&mut out);
} else {
self.fetch_headset_controller_state(&mut out);
}
self.fetch_tracking_state(&mut out);
}
out
}
}
struct InputCapabilities {
controller_capabilities: u32,
trackpad_max_x: u16,
trackpad_max_y: u16,
}
impl InputCapabilities {
pub fn from_ovr(ovr: *mut ovr::ovrMobile,
ovr_type: ovr::ovrControllerType,
ovr_id: ovr::ovrDeviceID) -> Result<InputCapabilities,()> {
if ovr_type == ovrControllerType_TrackedRemote {
let mut caps: ovr::ovrInputTrackedRemoteCapabilities = unsafe { mem::uninitialized() };
caps.Header.DeviceID = ovr_id;
caps.Header.Type = ovr_type;
let status = unsafe {
ovr::vrapi_GetInputDeviceCapabilities(ovr, &mut caps.Header)
};
if status != ovr::ovrSuccessResult::ovrSuccess as i32 {
return Err(());
}
Ok(Self {
controller_capabilities: caps.ControllerCapabilities,
trackpad_max_x: caps.TrackpadMaxX,
trackpad_max_y: caps.TrackpadMaxY,
})
} else {
let mut caps: ovr::ovrInputHeadsetCapabilities = unsafe { mem::uninitialized() };
caps.Header.DeviceID = ovr_id;
caps.Header.Type = ovr_type;
let status = unsafe {
ovr::vrapi_GetInputDeviceCapabilities(ovr, &mut caps.Header)
};
if status != ovr::ovrSuccessResult::ovrSuccess as i32 {
return Err(());
}
Ok(Self {
controller_capabilities: caps.ControllerCapabilities,
trackpad_max_x: caps.TrackpadMaxX,
trackpad_max_y: caps.TrackpadMaxY,
})
}
}
}
impl Default for InputCapabilities {
fn default() -> InputCapabilities {
InputCapabilities {
controller_capabilities: 0,
trackpad_max_x: 299,
trackpad_max_y: 199,
}
}
}