mirror of
https://github.com/mii443/rust-openvr.git
synced 2025-08-22 16:25:36 +00:00
Draft changes for OpenVR 1.0.7
This commit is contained in:
@ -1,3 +1,4 @@
|
|||||||
|
dist: trusty
|
||||||
language: rust
|
language: rust
|
||||||
rust:
|
rust:
|
||||||
- stable
|
- stable
|
||||||
|
@ -4,7 +4,8 @@ version = "0.4.0"
|
|||||||
authors = [
|
authors = [
|
||||||
"Colin Sherratt",
|
"Colin Sherratt",
|
||||||
"Erick Tryzelaar",
|
"Erick Tryzelaar",
|
||||||
"Rene Eichhorn"
|
"Rene Eichhorn",
|
||||||
|
"Benjamin Saunders"
|
||||||
]
|
]
|
||||||
license-file = "LICENSE.md"
|
license-file = "LICENSE.md"
|
||||||
|
|
||||||
@ -14,12 +15,8 @@ repository = "https://github.com/rust-openvr/rust-openvr"
|
|||||||
|
|
||||||
description = "A safe binding for openvr."
|
description = "A safe binding for openvr."
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "openvr"
|
|
||||||
path = "src/lib.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
openvr_sys = "1.0.2"
|
openvr_sys = { git = "https://github.com/Ralith/rust-openvr-sys.git", branch = "update" }
|
||||||
|
|
||||||
[dev_dependencies]
|
[dev_dependencies]
|
||||||
glium = "0.14.0"
|
glium = "0.14.0"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
Copyright (c) 2016 Colin Sherratt, Erick Tryzelaar, Rene Eichhorn
|
Copyright (c) 2016 Colin Sherratt, Erick Tryzelaar, Rene Eichhorn, Benjamin Saunders
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
@ -1,73 +1,199 @@
|
|||||||
use openvr_sys;
|
//! The `Compositor` implements distortion, prediction, synchronization and other subtle issues that can be a challenge to
|
||||||
use openvr_sys::EGraphicsAPIConvention::*;
|
//! get operating properly for a solid VR experience.
|
||||||
use openvr_sys::EVRSubmitFlags::*;
|
//!
|
||||||
use openvr_sys::EColorSpace::*;
|
//! Applications call WaitGetPoses to get the set of poses used to render the camera and other tracked objects, render
|
||||||
use common::*;
|
//! the left and right eyes as normal (using the info provided by `System`) and finally `submit` those undistorted
|
||||||
use tracking::*;
|
//! textures for the `Compositor` to display on the output device.
|
||||||
|
//!
|
||||||
|
//! It is recommended that you continue presenting your application's own window, reusing either the left or right eye
|
||||||
|
//! camera render target to draw a single quad (perhaps cropped to a lower fov to hide the hidden area mask).
|
||||||
|
|
||||||
/// A VR compositor
|
use std::{mem, ptr, error, fmt};
|
||||||
pub struct IVRCompositor(*const ());
|
use std::ffi::CString;
|
||||||
|
|
||||||
impl IVRCompositor {
|
use openvr_sys as sys;
|
||||||
pub unsafe fn from_raw(ptr: *const ()) -> Self {
|
|
||||||
IVRCompositor(ptr as *mut ())
|
use super::*;
|
||||||
|
|
||||||
|
impl<'a> Compositor<'a> {
|
||||||
|
pub fn vulkan_instance_extensions_required(&self) -> Vec<CString> {
|
||||||
|
let temp = unsafe {
|
||||||
|
let n = (self.0.GetVulkanInstanceExtensionsRequired.unwrap())(ptr::null_mut(), 0);
|
||||||
|
let mut buffer: Vec<u8> = Vec::new();
|
||||||
|
buffer.resize(n as usize, mem::uninitialized());
|
||||||
|
(self.0.GetVulkanInstanceExtensionsRequired.unwrap())(buffer.as_mut_ptr() as *mut i8, n);
|
||||||
|
buffer
|
||||||
|
};
|
||||||
|
temp.split(|&x| x == b' ').map(|x| CString::new(x.to_vec()).expect("extension name contained null byte")).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check to see if the compositor is fullscreen
|
pub fn vulkan_device_extensions_required(&self, physical_device: *mut VkPhysicalDevice_T) -> Vec<CString> {
|
||||||
pub fn is_fullscreen(&self) -> bool {
|
let temp = unsafe {
|
||||||
|
let n = (self.0.GetVulkanDeviceExtensionsRequired.unwrap())(physical_device, ptr::null_mut(), 0);
|
||||||
|
let mut buffer: Vec<u8> = Vec::new();
|
||||||
|
buffer.resize(n as usize, mem::uninitialized());
|
||||||
|
(self.0.GetVulkanDeviceExtensionsRequired.unwrap())(physical_device as *mut _, buffer.as_mut_ptr() as *mut i8, n);
|
||||||
|
buffer
|
||||||
|
};
|
||||||
|
temp.split(|&x| x == b' ').map(|x| CString::new(x.to_vec()).expect("extension name contained null byte")).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets tracking space returned by WaitGetPoses
|
||||||
|
pub fn set_tracking_space(&self, origin: TrackingUniverseOrigin) {
|
||||||
|
unsafe { (self.0.SetTrackingSpace.unwrap())(origin as sys::ETrackingUniverseOrigin) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Block until a few milliseconds before the next vsync, then return poses for the next step of rendering and game
|
||||||
|
/// logic.
|
||||||
|
///
|
||||||
|
/// Poses are relative to the origin set by `set_tracking_space`.
|
||||||
|
pub fn wait_get_poses(&self) -> Result<WaitPoses, CompositorError> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let comp = * { self.0 as *mut openvr_sys::VR_IVRCompositor_FnTable };
|
let mut result: WaitPoses = mem::uninitialized();
|
||||||
comp.IsFullscreen.unwrap()() > 0
|
let e = (self.0.WaitGetPoses.unwrap())(result.render.data.as_mut().as_mut_ptr() as *mut _, result.render.data.len() as u32,
|
||||||
|
result.game.data.as_mut().as_mut_ptr() as *mut _, result.game.data.len() as u32);
|
||||||
|
if e == sys::EVRCompositorError_EVRCompositorError_VRCompositorError_None {
|
||||||
|
Ok(result)
|
||||||
|
} else {
|
||||||
|
Err(CompositorError(e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if compositor can render a scene
|
/// Display the supplied texture for the next frame.
|
||||||
pub fn can_render_scene(&self) -> bool {
|
///
|
||||||
unsafe {
|
/// If `bounds` is None, the entire texture will be used. Lens distortion is handled by the OpenVR implementation.
|
||||||
let comp = * { self.0 as *mut openvr_sys::VR_IVRCompositor_FnTable };
|
pub fn submit(&self, eye: Eye, texture: &Texture, bounds: Option<&TextureBounds>) -> Result<(), CompositorError> {
|
||||||
comp.CanRenderScene.unwrap()() > 0
|
use self::TextureHandle::*;
|
||||||
|
let texture = sys::Texture_t {
|
||||||
|
handle: match texture.handle {
|
||||||
|
Vulkan(ref x) => x as *const _ as *mut _,
|
||||||
|
},
|
||||||
|
eType: match texture.handle {
|
||||||
|
Vulkan(_) => sys::ETextureType_ETextureType_TextureType_Vulkan,
|
||||||
|
},
|
||||||
|
eColorSpace: texture.color_space as sys::EColorSpace,
|
||||||
|
};
|
||||||
|
let e = unsafe {
|
||||||
|
(self.0.Submit.unwrap())(eye as sys::EVREye,
|
||||||
|
&texture as *const _ as *mut _,
|
||||||
|
bounds.map(|x| x as *const _ as *mut TextureBounds as *mut _).unwrap_or(ptr::null_mut()),
|
||||||
|
sys::EVRSubmitFlags_EVRSubmitFlags_Submit_Default)
|
||||||
|
};
|
||||||
|
if e == sys::EVRCompositorError_EVRCompositorError_VRCompositorError_None {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(CompositorError(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Submits an opengl framebuffer as an eye to the render
|
pub fn post_present_handoff(&self) {
|
||||||
pub fn submit(&self, eye: Eye, texture: usize, bounds: TextureBounds) {
|
unsafe { (self.0.PostPresentHandoff.unwrap())() };
|
||||||
let mut b = bounds.to_raw();
|
|
||||||
let e = eye.to_raw();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
use std;
|
|
||||||
|
|
||||||
let comp = * { self.0 as *mut openvr_sys::VR_IVRCompositor_FnTable };
|
|
||||||
let mut t = openvr_sys::Texture_t {
|
|
||||||
eType: EGraphicsAPIConvention_API_OpenGL,
|
|
||||||
eColorSpace: EColorSpace_ColorSpace_Auto,
|
|
||||||
handle: texture as *mut std::os::raw::c_void,
|
|
||||||
};
|
|
||||||
|
|
||||||
comp.Submit.unwrap()(
|
|
||||||
e,
|
|
||||||
&mut t,
|
|
||||||
&mut b as *mut openvr_sys::VRTextureBounds_t,
|
|
||||||
EVRSubmitFlags_Submit_GlRenderBuffer
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the poses
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub fn wait_get_poses(&self) -> TrackedDevicePoses {
|
pub struct WaitPoses {
|
||||||
use std;
|
/// Predicted to the point they will be at the upcoming frame.
|
||||||
|
pub render: TrackedDevicePoses,
|
||||||
|
/// Predicted to the point they will be at the frame after the upcoming frame, for use in game logic.
|
||||||
|
pub game: TrackedDevicePoses,
|
||||||
|
}
|
||||||
|
|
||||||
unsafe {
|
pub use sys::VkPhysicalDevice_T;
|
||||||
let comp = * { self.0 as *mut openvr_sys::VR_IVRCompositor_FnTable };
|
pub use sys::VkDevice_T;
|
||||||
let mut data: [openvr_sys::TrackedDevicePose_t; 16] = std::mem::zeroed();
|
pub use sys::VkInstance_T;
|
||||||
|
pub use sys::VkQueue_T;
|
||||||
|
|
||||||
comp.WaitGetPoses.unwrap()(
|
#[derive(Debug, Copy, Clone)]
|
||||||
&mut data[0],
|
pub struct Texture {
|
||||||
16,
|
pub handle: TextureHandle,
|
||||||
std::ptr::null_mut(),
|
pub color_space: ColorSpace,
|
||||||
0
|
}
|
||||||
);
|
|
||||||
to_tracked(data)
|
pub mod vulkan {
|
||||||
|
use super::*;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Texture {
|
||||||
|
pub image: u64,
|
||||||
|
pub device: *mut VkDevice_T,
|
||||||
|
pub physical_device: *mut VkPhysicalDevice_T,
|
||||||
|
pub instance: *mut VkInstance_T,
|
||||||
|
pub queue: *mut VkQueue_T,
|
||||||
|
pub queue_family_index: u32,
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
pub format: u32,
|
||||||
|
pub sample_count: u32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum TextureHandle {
|
||||||
|
Vulkan(vulkan::Texture),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
pub enum ColorSpace {
|
||||||
|
Auto = sys::EColorSpace_EColorSpace_ColorSpace_Auto as isize,
|
||||||
|
Gamma = sys::EColorSpace_EColorSpace_ColorSpace_Gamma as isize,
|
||||||
|
Linear = sys::EColorSpace_EColorSpace_ColorSpace_Linear as isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct TextureBounds {
|
||||||
|
pub umin: f32,
|
||||||
|
pub vmin: f32,
|
||||||
|
pub umax: f32,
|
||||||
|
pub vmax: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||||
|
pub struct CompositorError(sys::EVRCompositorError);
|
||||||
|
|
||||||
|
pub mod compositor_error {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub const REQUEST_FAILED: CompositorError = CompositorError(sys::EVRCompositorError_EVRCompositorError_VRCompositorError_RequestFailed);
|
||||||
|
pub const INCOMPATIBLE_VERSION: CompositorError = CompositorError(sys::EVRCompositorError_EVRCompositorError_VRCompositorError_IncompatibleVersion);
|
||||||
|
pub const DO_NOT_HAVE_FOCUS: CompositorError = CompositorError(sys::EVRCompositorError_EVRCompositorError_VRCompositorError_DoNotHaveFocus);
|
||||||
|
pub const INVALID_TEXTURE: CompositorError = CompositorError(sys::EVRCompositorError_EVRCompositorError_VRCompositorError_InvalidTexture);
|
||||||
|
pub const IS_NOT_SCENE_APPLICATION: CompositorError = CompositorError(sys::EVRCompositorError_EVRCompositorError_VRCompositorError_IsNotSceneApplication);
|
||||||
|
pub const TEXTURE_IS_ON_WRONG_DEVICE: CompositorError = CompositorError(sys::EVRCompositorError_EVRCompositorError_VRCompositorError_TextureIsOnWrongDevice);
|
||||||
|
pub const TEXTURE_USES_UNSUPPORTED_FORMAT: CompositorError = CompositorError(sys::EVRCompositorError_EVRCompositorError_VRCompositorError_TextureUsesUnsupportedFormat);
|
||||||
|
pub const SHARED_TEXTURES_NOT_SUPPORTED: CompositorError = CompositorError(sys::EVRCompositorError_EVRCompositorError_VRCompositorError_SharedTexturesNotSupported);
|
||||||
|
pub const INDEX_OUT_OF_RANGE: CompositorError = CompositorError(sys::EVRCompositorError_EVRCompositorError_VRCompositorError_IndexOutOfRange);
|
||||||
|
pub const ALREADY_SUBMITTED: CompositorError = CompositorError(sys::EVRCompositorError_EVRCompositorError_VRCompositorError_AlreadySubmitted);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for CompositorError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.pad(error::Error::description(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::Error for CompositorError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
use self::compositor_error::*;
|
||||||
|
match *self {
|
||||||
|
REQUEST_FAILED => "REQUEST_FAILED",
|
||||||
|
INCOMPATIBLE_VERSION => "INCOMPATIBLE_VERSION",
|
||||||
|
DO_NOT_HAVE_FOCUS => "DO_NOT_HAVE_FOCUS",
|
||||||
|
INVALID_TEXTURE => "INVALID_TEXTURE",
|
||||||
|
IS_NOT_SCENE_APPLICATION => "IS_NOT_SCENE_APPLICATION",
|
||||||
|
TEXTURE_IS_ON_WRONG_DEVICE => "TEXTURE_IS_ON_WRONG_DEVICE",
|
||||||
|
TEXTURE_USES_UNSUPPORTED_FORMAT => "TEXTURE_USES_UNSUPPORTED_FORMAT",
|
||||||
|
SHARED_TEXTURES_NOT_SUPPORTED => "SHARED_TEXTURES_NOT_SUPPORTED",
|
||||||
|
INDEX_OUT_OF_RANGE => "INDEX_OUT_OF_RANGE",
|
||||||
|
ALREADY_SUBMITTED => "ALREADY_SUBMITTED",
|
||||||
|
_ => "UNKNOWN",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for CompositorError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.pad(error::Error::description(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
119
src/error.rs
119
src/error.rs
@ -1,119 +0,0 @@
|
|||||||
use openvr_sys;
|
|
||||||
use subsystems::*;
|
|
||||||
|
|
||||||
pub trait RawError {
|
|
||||||
fn is_err(&self) -> bool;
|
|
||||||
fn message(&self) -> String;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Error<Err: RawError + Copy> {
|
|
||||||
raw: Err
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Err: RawError + Copy> Error<Err> {
|
|
||||||
/// Creates a new error object using the raw openvr_sys error
|
|
||||||
pub fn from_raw(raw: Err) -> Self {
|
|
||||||
Error {
|
|
||||||
raw: raw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Turns managed error into raw enum from binding
|
|
||||||
pub fn to_raw(&self) -> Err {
|
|
||||||
self.raw
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets an human-readable error message (if available)
|
|
||||||
pub fn message(&self) -> String {
|
|
||||||
self.raw.message()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true when current object is not an error
|
|
||||||
pub fn is_ok(&self) -> bool {
|
|
||||||
!self.raw.is_err()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return true when current object is an error
|
|
||||||
pub fn is_err(&self) -> bool {
|
|
||||||
self.raw.is_err()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenVR implement per error type a new function to get a error string
|
|
||||||
// for easier use, this macro will generate easily the RawError trait
|
|
||||||
macro_rules! impl_raw_error {
|
|
||||||
($subsystem:ident, $fntable: ident, $get:ident, $raw_name:ident, $none_name:ident) => {
|
|
||||||
impl RawError for $raw_name {
|
|
||||||
fn is_err(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
$none_name => {
|
|
||||||
false
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn message(&self) -> String {
|
|
||||||
let sstr = unsafe {
|
|
||||||
let sub = * { $subsystem().unwrap().0 as *mut openvr_sys::$fntable};
|
|
||||||
CStr::from_ptr(sub.$get.unwrap()(*self)).to_str().unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
String::from(sstr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use std::ffi::CStr;
|
|
||||||
use openvr_sys::*;
|
|
||||||
use openvr_sys::ETrackedPropertyError::*;
|
|
||||||
use openvr_sys::EVRInitError::*;
|
|
||||||
use openvr_sys::EVRRenderModelError::*;
|
|
||||||
use openvr_sys::EVRTrackedCameraError::*;
|
|
||||||
|
|
||||||
impl_raw_error!(
|
|
||||||
system,
|
|
||||||
VR_IVRSystem_FnTable,
|
|
||||||
GetPropErrorNameFromEnum,
|
|
||||||
ETrackedPropertyError,
|
|
||||||
ETrackedPropertyError_TrackedProp_Success);
|
|
||||||
|
|
||||||
impl_raw_error!(
|
|
||||||
render_models,
|
|
||||||
VR_IVRRenderModels_FnTable,
|
|
||||||
GetRenderModelErrorNameFromEnum,
|
|
||||||
EVRRenderModelError,
|
|
||||||
EVRRenderModelError_VRRenderModelError_None);
|
|
||||||
|
|
||||||
impl_raw_error!(
|
|
||||||
tracked_camera,
|
|
||||||
VR_IVRTrackedCamera_FnTable,
|
|
||||||
GetCameraErrorNameFromEnum,
|
|
||||||
EVRTrackedCameraError,
|
|
||||||
EVRTrackedCameraError_VRTrackedCameraError_None);
|
|
||||||
|
|
||||||
// The init error has some special function to retrieve string
|
|
||||||
impl RawError for EVRInitError {
|
|
||||||
fn is_err(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
EVRInitError_VRInitError_None => {
|
|
||||||
true
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn message(&self) -> String {
|
|
||||||
let sstr = unsafe {
|
|
||||||
CStr::from_ptr(openvr_sys::VR_GetVRInitErrorAsEnglishDescription(*self)).to_str().unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
String::from(sstr)
|
|
||||||
}
|
|
||||||
}
|
|
179
src/lib.rs
179
src/lib.rs
@ -1,62 +1,143 @@
|
|||||||
extern crate openvr_sys;
|
extern crate openvr_sys;
|
||||||
|
|
||||||
use openvr_sys::EVRInitError::*;
|
use std::sync::atomic::{Ordering, AtomicBool, ATOMIC_BOOL_INIT};
|
||||||
use openvr_sys::EVRApplicationType::*;
|
use std::{fmt, error, slice};
|
||||||
|
use std::ffi::CStr;
|
||||||
|
|
||||||
pub mod common;
|
use openvr_sys as sys;
|
||||||
pub mod error;
|
|
||||||
pub mod tracking;
|
|
||||||
pub mod system;
|
|
||||||
pub mod extended_display;
|
|
||||||
pub mod compositor;
|
|
||||||
pub mod render_models;
|
|
||||||
pub mod tracked_camera;
|
|
||||||
pub mod subsystems;
|
|
||||||
|
|
||||||
pub use system::IVRSystem;
|
mod tracking;
|
||||||
pub use extended_display::IVRExtendedDisplay;
|
|
||||||
pub use compositor::IVRCompositor;
|
|
||||||
pub use render_models::IVRRenderModels;
|
|
||||||
pub use tracked_camera::IVRTrackedCamera;
|
|
||||||
|
|
||||||
pub use subsystems::*;
|
mod system;
|
||||||
pub use error::*;
|
mod compositor;
|
||||||
|
|
||||||
pub use common::Eye;
|
pub use tracking::*;
|
||||||
|
|
||||||
/// Inits the open vr interface and returns the system
|
static INITIALIZED: AtomicBool = ATOMIC_BOOL_INIT;
|
||||||
pub fn init() -> Result<system::IVRSystem, Error<openvr_sys::EVRInitError>> {
|
|
||||||
let mut err = EVRInitError_VRInitError_None;
|
|
||||||
let app_type = EVRApplicationType_VRApplication_Scene;
|
|
||||||
|
|
||||||
// try to initialize base vr eco
|
/// Initialize OpenVR
|
||||||
unsafe {
|
///
|
||||||
openvr_sys::VR_InitInternal(&mut err, app_type);
|
/// # Panics
|
||||||
};
|
/// When the library has already been initialized
|
||||||
|
pub fn init(ty: ApplicationType) -> Result<Context, InitError> {
|
||||||
|
if INITIALIZED.swap(true, Ordering::AcqRel) {
|
||||||
|
panic!("OpenVR has already been initialized!");
|
||||||
|
}
|
||||||
|
|
||||||
// check for errors
|
let mut error = sys::EVRInitError_EVRInitError_VRInitError_None;
|
||||||
match err {
|
unsafe { sys::VR_InitInternal(&mut error, ty as sys::EVRApplicationType) };
|
||||||
EVRInitError_VRInitError_None => {
|
if error != sys::EVRInitError_EVRInitError_VRInitError_None {
|
||||||
// get system
|
return Err(InitError(error));
|
||||||
let result = system();
|
}
|
||||||
match result {
|
if !unsafe { sys::VR_IsInterfaceVersionValid(sys::IVRSystem_Version.as_ptr() as *const i8) } {
|
||||||
Ok(sys) => {
|
unsafe { sys::VR_ShutdownInternal() }
|
||||||
return Ok(sys);
|
return Err(InitError(sys::EVRInitError_EVRInitError_VRInitError_Init_InterfaceNotFound));
|
||||||
},
|
}
|
||||||
Err(err) => {
|
Ok(unsafe { Context::new() }?)
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
return Err(Error::from_raw(err));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shutdowns all openvr related systems
|
pub struct System<'a>(&'a sys::VR_IVRSystem_FnTable);
|
||||||
pub fn shutdown() {
|
pub struct Compositor<'a>(&'a sys::VR_IVRCompositor_FnTable);
|
||||||
unsafe {
|
|
||||||
openvr_sys::VR_ShutdownInternal();
|
/// Entry points into OpenVR.
|
||||||
|
///
|
||||||
|
/// At most one of this object may exist at a time.
|
||||||
|
pub struct Context {
|
||||||
|
system: *const sys::VR_IVRSystem_FnTable,
|
||||||
|
compositor: *const sys::VR_IVRCompositor_FnTable,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
/// Must be called after sys::VR_InitInternal
|
||||||
|
unsafe fn new() -> Result<Self, InitError> {
|
||||||
|
fn load<T>(suffix: &[u8]) -> Result<*const T, InitError> {
|
||||||
|
let mut magic = Vec::from(b"FnTable:".as_ref());
|
||||||
|
magic.extend(suffix);
|
||||||
|
let mut error = sys::EVRInitError_EVRInitError_VRInitError_None;
|
||||||
|
let result = unsafe { sys::VR_GetGenericInterface(magic.as_ptr() as *const i8, &mut error) };
|
||||||
|
if error != sys::EVRInitError_EVRInitError_VRInitError_None {
|
||||||
|
return Err(InitError(sys::EVRInitError_EVRInitError_VRInitError_Init_InterfaceNotFound));
|
||||||
|
}
|
||||||
|
Ok(result as *const T)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Context {
|
||||||
|
system: load(sys::IVRSystem_Version)?,
|
||||||
|
compositor: load(sys::IVRCompositor_Version)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn system(&self) -> System { unsafe { System(&*self.system) } }
|
||||||
|
pub fn compositor(&self) -> Compositor { unsafe { Compositor(&*self.compositor) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Context {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { sys::VR_ShutdownInternal() }
|
||||||
|
INITIALIZED.store(false, Ordering::AcqRel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
pub enum ApplicationType {
|
||||||
|
/// Some other kind of application that isn't covered by the other entries
|
||||||
|
Other = sys::EVRApplicationType_EVRApplicationType_VRApplication_Other as isize,
|
||||||
|
/// Application will submit 3D frames
|
||||||
|
Scene = sys::EVRApplicationType_EVRApplicationType_VRApplication_Scene as isize,
|
||||||
|
/// Application only interacts with overlays
|
||||||
|
Overlay = sys::EVRApplicationType_EVRApplicationType_VRApplication_Overlay as isize,
|
||||||
|
/// Application should not start SteamVR if it's not already running, and should not keep it running if everything
|
||||||
|
/// else quits.
|
||||||
|
Background = sys::EVRApplicationType_EVRApplicationType_VRApplication_Background as isize,
|
||||||
|
/// Init should not try to load any drivers. The application needs access to utility interfaces (like IVRSettings
|
||||||
|
/// and IVRApplications) but not hardware.
|
||||||
|
Utility = sys::EVRApplicationType_EVRApplicationType_VRApplication_Utility as isize,
|
||||||
|
/// Reserved for vrmonitor
|
||||||
|
VRMonitor = sys::EVRApplicationType_EVRApplicationType_VRApplication_VRMonitor as isize,
|
||||||
|
/// Reserved for Steam
|
||||||
|
SteamWatchdog = sys::EVRApplicationType_EVRApplicationType_VRApplication_SteamWatchdog as isize,
|
||||||
|
/// Start up SteamVR
|
||||||
|
Bootstrapper = sys::EVRApplicationType_EVRApplicationType_VRApplication_Bootstrapper as isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InitError(sys::EVRInitError);
|
||||||
|
|
||||||
|
impl fmt::Debug for InitError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let msg = unsafe {
|
||||||
|
CStr::from_ptr(sys::VR_GetVRInitErrorAsSymbol(self.0))
|
||||||
|
};
|
||||||
|
f.pad(msg.to_str().expect("OpenVR init error symbol was not valid UTF-8"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::Error for InitError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
let msg = unsafe {
|
||||||
|
CStr::from_ptr(sys::VR_GetVRInitErrorAsEnglishDescription(self.0))
|
||||||
|
};
|
||||||
|
msg.to_str().expect("OpenVR init error description was not valid UTF-8")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for InitError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.pad(error::Error::description(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
pub enum Eye {
|
||||||
|
Left = sys::EVREye_EVREye_Eye_Left as isize,
|
||||||
|
Right = sys::EVREye_EVREye_Eye_Right as isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct TrackedDevicePoses {
|
||||||
|
data: [TrackedDevicePose; sys::k_unMaxTrackedDeviceCount as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TrackedDevicePoses {
|
||||||
|
pub fn iter(&self) -> slice::Iter<TrackedDevicePose> { self.data.iter() }
|
||||||
|
pub fn len(&self) -> usize { self.data.len() }
|
||||||
|
}
|
||||||
|
@ -1,111 +0,0 @@
|
|||||||
extern crate openvr_sys;
|
|
||||||
use openvr_sys::EVRInitError::*;
|
|
||||||
|
|
||||||
use error::*;
|
|
||||||
use system::IVRSystem;
|
|
||||||
use extended_display::IVRExtendedDisplay;
|
|
||||||
use compositor::IVRCompositor;
|
|
||||||
use render_models::IVRRenderModels;
|
|
||||||
use tracked_camera::IVRTrackedCamera;
|
|
||||||
|
|
||||||
use std;
|
|
||||||
|
|
||||||
/// gets the current vr system interface (initialization is required beforehand)
|
|
||||||
pub fn system() -> Result<IVRSystem, Error<openvr_sys::EVRInitError>> {
|
|
||||||
let mut err = EVRInitError_VRInitError_None;
|
|
||||||
let name = std::ffi::CString::new("FnTable:IVRSystem_012").unwrap();
|
|
||||||
let ptr = unsafe {
|
|
||||||
openvr_sys::VR_GetGenericInterface(name.as_ptr(), &mut err)
|
|
||||||
};
|
|
||||||
|
|
||||||
match err {
|
|
||||||
EVRInitError_VRInitError_None => {
|
|
||||||
unsafe {
|
|
||||||
return Ok(IVRSystem::from_raw(ptr as *const ()));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
return Err(Error::from_raw(err));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// gets the current vr extended display interface (initialization is required beforehand)
|
|
||||||
pub fn extended_display() -> Result<IVRExtendedDisplay, Error<openvr_sys::EVRInitError>> {
|
|
||||||
let mut err = EVRInitError_VRInitError_None;
|
|
||||||
let name = std::ffi::CString::new("FnTable:IVRExtendedDisplay_001").unwrap();
|
|
||||||
let ptr = unsafe {
|
|
||||||
openvr_sys::VR_GetGenericInterface(name.as_ptr(), &mut err)
|
|
||||||
};
|
|
||||||
|
|
||||||
match err {
|
|
||||||
EVRInitError_VRInitError_None => {
|
|
||||||
unsafe {
|
|
||||||
return Ok(IVRExtendedDisplay::from_raw(ptr as *const ()));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
return Err(Error::from_raw(err));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// gets the current vr extended display interface (initialization is required beforehand)
|
|
||||||
pub fn compositor() -> Result<IVRCompositor, Error<openvr_sys::EVRInitError>> {
|
|
||||||
let mut err = EVRInitError_VRInitError_None;
|
|
||||||
let name = std::ffi::CString::new("FnTable:IVRCompositor_013").unwrap();
|
|
||||||
let ptr = unsafe {
|
|
||||||
openvr_sys::VR_GetGenericInterface(name.as_ptr(), &mut err)
|
|
||||||
};
|
|
||||||
|
|
||||||
match err {
|
|
||||||
EVRInitError_VRInitError_None => {
|
|
||||||
unsafe {
|
|
||||||
return Ok(IVRCompositor::from_raw(ptr as *const ()));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
return Err(Error::from_raw(err));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// gets the current vr extended display interface (initialization is required beforehand)
|
|
||||||
pub fn render_models() -> Result<IVRRenderModels, Error<openvr_sys::EVRInitError>> {
|
|
||||||
let mut err = EVRInitError_VRInitError_None;
|
|
||||||
let name = std::ffi::CString::new("FnTable:IVRRenderModels_005").unwrap();
|
|
||||||
let ptr = unsafe {
|
|
||||||
openvr_sys::VR_GetGenericInterface(name.as_ptr(), &mut err)
|
|
||||||
};
|
|
||||||
|
|
||||||
match err {
|
|
||||||
EVRInitError_VRInitError_None => {
|
|
||||||
unsafe {
|
|
||||||
return Ok(IVRRenderModels::from_raw(ptr as *const ()));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
return Err(Error::from_raw(err));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// gets the current vr extended display interface (initialization is required beforehand)
|
|
||||||
pub fn tracked_camera() -> Result<IVRTrackedCamera, Error<openvr_sys::EVRInitError>> {
|
|
||||||
let mut err = EVRInitError_VRInitError_None;
|
|
||||||
let name = std::ffi::CString::new("FnTable:IVRTrackedCamera_003").unwrap();
|
|
||||||
let ptr = unsafe {
|
|
||||||
openvr_sys::VR_GetGenericInterface(name.as_ptr(), &mut err)
|
|
||||||
};
|
|
||||||
|
|
||||||
match err {
|
|
||||||
EVRInitError_VRInitError_None => {
|
|
||||||
unsafe {
|
|
||||||
return Ok(IVRTrackedCamera::from_raw(ptr as *const ()));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
return Err(Error::from_raw(err));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
643
src/system.rs
643
src/system.rs
@ -1,112 +1,587 @@
|
|||||||
use openvr_sys;
|
//! The `System` interface provides access to display configuration information, tracking data, controller state,
|
||||||
use openvr_sys::EGraphicsAPIConvention::*;
|
//! events, and device properties. It is the main interface of OpenVR.
|
||||||
use openvr_sys::ETrackingUniverseOrigin::*;
|
|
||||||
|
|
||||||
use common::*;
|
use std::mem;
|
||||||
use tracking::*;
|
|
||||||
|
|
||||||
pub struct IVRSystem(pub *const ());
|
use openvr_sys as sys;
|
||||||
|
|
||||||
impl IVRSystem {
|
use super::*;
|
||||||
pub unsafe fn from_raw(ptr: *const ()) -> Self {
|
|
||||||
IVRSystem(ptr as *mut ())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the recommended render target size
|
impl<'a> System<'a> {
|
||||||
pub fn recommended_render_target_size(&self) -> Size {
|
/// Provides the game with the minimum size that it should use for its offscreen render target to minimize pixel
|
||||||
|
/// stretching. This size is matched with the projection matrix and distortion function and will change from display
|
||||||
|
/// to display depending on resolution, distortion, and field of view.
|
||||||
|
pub fn recommended_render_target_size(&self) -> (u32, u32) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let system = * { self.0 as *mut openvr_sys::VR_IVRSystem_FnTable };
|
let mut result: (u32, u32) = mem::uninitialized();
|
||||||
|
(self.0.GetRecommendedRenderTargetSize.unwrap())(&mut result.0, &mut result.1);
|
||||||
let mut size = Size{width: 0, height: 0};
|
result
|
||||||
system.GetRecommendedRenderTargetSize.unwrap()(
|
|
||||||
&mut size.width,
|
|
||||||
&mut size.height
|
|
||||||
);
|
|
||||||
size
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the projection matrix to use for the specified eye.
|
||||||
|
///
|
||||||
|
/// Clip plane distances are in meters.
|
||||||
|
pub fn projection_matrix(&self, eye: Eye, near_z: f32, far_z: f32) -> [[f32; 4]; 4] {
|
||||||
|
unsafe { (self.0.GetProjectionMatrix.unwrap())(eye as sys::EVREye, near_z, far_z) }.m
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the projection matrix for an eye
|
/// Returns the raw project values to use for the specified eye. Most games should use GetProjectionMatrix instead
|
||||||
/// supply the near and the far position
|
/// of this method, but sometimes a game needs to do something fancy with its projection and can use these values to
|
||||||
/// assumes opengl conventions
|
/// compute its own matrix.
|
||||||
pub fn projection_matrix(&self, eye: Eye, near: f32, far: f32) -> [[f32; 4]; 4] {
|
pub fn projection_raw(&self, eye: Eye) -> RawProjection {
|
||||||
unsafe {
|
unsafe {
|
||||||
let system = * { self.0 as *mut openvr_sys::VR_IVRSystem_FnTable };
|
let mut result: RawProjection = mem::uninitialized();
|
||||||
|
(self.0.GetProjectionRaw.unwrap())(eye as sys::EVREye, &mut result.left, &mut result.right, &mut result.top, &mut result.bottom);
|
||||||
let mat = system.GetProjectionMatrix.unwrap()(
|
result
|
||||||
eye.to_raw(),
|
|
||||||
near,
|
|
||||||
far,
|
|
||||||
EGraphicsAPIConvention_API_OpenGL
|
|
||||||
);
|
|
||||||
mat.m
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the distortion caused by the optics
|
/// Returns the transform between the view space and eye space. Eye space is the per-eye flavor of view space that
|
||||||
pub fn compute_distortion(&self, eye: Eye, u: f32, v: f32) -> DistortionCoordinates {
|
/// provides stereo disparity. Instead of Model * View * Projection the model is Model * View * Eye *
|
||||||
unsafe {
|
/// Projection. Normally View and Eye will be multiplied together and treated as View in your application.
|
||||||
let system = * { self.0 as *mut openvr_sys::VR_IVRSystem_FnTable };
|
|
||||||
let coord = system.ComputeDistortion.unwrap()(
|
|
||||||
eye.to_raw(),
|
|
||||||
u, v
|
|
||||||
);
|
|
||||||
DistortionCoordinates {
|
|
||||||
red: coord.rfRed,
|
|
||||||
blue: coord.rfBlue,
|
|
||||||
green: coord.rfGreen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes the distortion caused by the optics
|
|
||||||
pub fn eye_to_head_transform(&self, eye: Eye) -> [[f32; 4]; 3] {
|
pub fn eye_to_head_transform(&self, eye: Eye) -> [[f32; 4]; 3] {
|
||||||
unsafe {
|
unsafe { (self.0.GetEyeToHeadTransform.unwrap())(eye as sys::EVREye) }.m
|
||||||
let system = * { self.0 as *mut openvr_sys::VR_IVRSystem_FnTable };
|
|
||||||
let mat = system.GetEyeToHeadTransform.unwrap()(
|
|
||||||
eye.to_raw(),
|
|
||||||
);
|
|
||||||
mat.m
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the distortion caused by the optics
|
/// Returns the number of elapsed seconds since the last recorded vsync event and the global number of frames that
|
||||||
|
/// have been rendered. Timing information will come from a vsync timer event in the timer if possible or from the
|
||||||
|
/// application-reported time if that is not available. If no vsync times are available the function will return
|
||||||
|
/// None.
|
||||||
pub fn time_since_last_vsync(&self) -> Option<(f32, u64)> {
|
pub fn time_since_last_vsync(&self) -> Option<(f32, u64)> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let system = * { self.0 as *mut openvr_sys::VR_IVRSystem_FnTable };
|
let mut result: (f32, u64) = mem::uninitialized();
|
||||||
let mut frame = 0;
|
if (self.0.GetTimeSinceLastVsync.unwrap())(&mut result.0, &mut result.1) {
|
||||||
let mut sync = 0.;
|
Some(result)
|
||||||
let found = system.GetTimeSinceLastVsync.unwrap()(
|
|
||||||
&mut sync,
|
|
||||||
&mut frame
|
|
||||||
);
|
|
||||||
|
|
||||||
if found > 0 {
|
|
||||||
Some((sync, frame))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch the tracked results from the HMD
|
/// Calculates updated poses for all devices.
|
||||||
/// when time is bigger than 0, it will give you the predicted poses for that time
|
///
|
||||||
/// Time is counted in photons, see https://github.com/ValveSoftware/openvr/wiki/IVRSystem::GetDeviceToAbsoluteTrackingPose
|
/// The pose that the tracker thinks that the HMD will be in at the specified number of seconds into the
|
||||||
/// for time to photons conversion
|
/// future. Pass 0 to get the state at the instant the method is called. Most of the time the application should
|
||||||
pub fn tracked_devices(&self, time: f32) -> TrackedDevicePoses {
|
/// calculate the time until the photons will be emitted from the display and pass that time into the method.
|
||||||
use std;
|
///
|
||||||
|
/// This is roughly analogous to the inverse of the view matrix in most applications, though many games will need to
|
||||||
|
/// do some additional rotation or translation on top of the rotation and translation provided by the head pose.
|
||||||
|
///
|
||||||
|
/// Seated experiences should call this method with TrackingUniverseSeated and receive poses relative to the seated
|
||||||
|
/// zero pose. Standing experiences should call this method with TrackingUniverseStanding and receive poses relative
|
||||||
|
/// to the chaperone soft bounds. TrackingUniverseRawAndUncalibrated should probably not be used unless the
|
||||||
|
/// application is the chaperone calibration tool itself, but will provide poses relative to the hardware-specific
|
||||||
|
/// coordinate system in the driver.
|
||||||
|
pub fn device_to_absolute_tracking_pose(&self, origin: TrackingUniverseOrigin, predicted_seconds_to_photons_from_now: f32) -> TrackedDevicePoses {
|
||||||
unsafe {
|
unsafe {
|
||||||
let system = * { self.0 as *mut openvr_sys::VR_IVRSystem_FnTable };
|
let mut result: TrackedDevicePoses = mem::uninitialized();
|
||||||
let mut data: [openvr_sys::TrackedDevicePose_t; 16] = std::mem::zeroed();
|
(self.0.GetDeviceToAbsoluteTrackingPose.unwrap())(origin as sys::ETrackingUniverseOrigin, predicted_seconds_to_photons_from_now,
|
||||||
system.GetDeviceToAbsoluteTrackingPose.unwrap()(
|
result.data.as_mut().as_mut_ptr() as *mut _, result.data.len() as u32);
|
||||||
ETrackingUniverseOrigin_TrackingUniverseSeated,
|
result
|
||||||
time,
|
}
|
||||||
&mut data[0],
|
}
|
||||||
16
|
|
||||||
);
|
pub fn tracked_device_class(&self, index: TrackedDeviceIndex) -> TrackedDeviceClass {
|
||||||
to_tracked(data)
|
use self::TrackedDeviceClass::*;
|
||||||
|
match unsafe { (self.0.GetTrackedDeviceClass.unwrap())(index) } {
|
||||||
|
sys::ETrackedDeviceClass_ETrackedDeviceClass_TrackedDeviceClass_Invalid => Invalid,
|
||||||
|
sys::ETrackedDeviceClass_ETrackedDeviceClass_TrackedDeviceClass_HMD => HMD,
|
||||||
|
sys::ETrackedDeviceClass_ETrackedDeviceClass_TrackedDeviceClass_Controller => Controller,
|
||||||
|
sys::ETrackedDeviceClass_ETrackedDeviceClass_TrackedDeviceClass_GenericTracker => GenericTracker,
|
||||||
|
sys::ETrackedDeviceClass_ETrackedDeviceClass_TrackedDeviceClass_TrackingReference => TrackingReference,
|
||||||
|
sys::ETrackedDeviceClass_ETrackedDeviceClass_TrackedDeviceClass_DisplayRedirect => DisplayRedirect,
|
||||||
|
_ => Invalid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_tracked_device_connected(&self, index: TrackedDeviceIndex) -> bool {
|
||||||
|
unsafe { (self.0.IsTrackedDeviceConnected.unwrap())(index) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn poll_next_event_with_pose(&self, origin: TrackingUniverseOrigin) -> Option<(EventInfo, TrackedDevicePose)> {
|
||||||
|
let mut event = unsafe { mem::uninitialized() };
|
||||||
|
let mut pose = unsafe { mem::uninitialized() };
|
||||||
|
if unsafe { self.0.PollNextEventWithPose.unwrap()(origin as sys::ETrackingUniverseOrigin,
|
||||||
|
&mut event, mem::size_of_val(&event) as u32,
|
||||||
|
&mut pose as *mut _ as *mut _) }
|
||||||
|
{
|
||||||
|
Some((EventInfo {
|
||||||
|
tracked_device_index: event.trackedDeviceIndex,
|
||||||
|
age: event.eventAgeSeconds,
|
||||||
|
event: Event::from_sys(event.eventType, &event.data)
|
||||||
|
}, pose))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Values represent the tangents of the half-angles from the center view axis
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct RawProjection {
|
||||||
|
/// tangent of the half-angle from center axis to the left clipping plane
|
||||||
|
pub left: f32,
|
||||||
|
/// tangent of the half-angle from center axis to the right clipping plane
|
||||||
|
pub right: f32,
|
||||||
|
/// tangent of the half-angle from center axis to the top clipping plane
|
||||||
|
pub top: f32,
|
||||||
|
/// tangent of the half-angle from center axis to the bottom clipping plane
|
||||||
|
pub bottom: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EventInfo {
|
||||||
|
/// The tracked device index of the event. For events that aren't connected to a tracked device this is
|
||||||
|
/// k_unTrackedDeviceIndexInvalid
|
||||||
|
pub tracked_device_index: TrackedDeviceIndex,
|
||||||
|
|
||||||
|
/// The age of the event in seconds.
|
||||||
|
pub age: f32,
|
||||||
|
|
||||||
|
/// More information about the event.
|
||||||
|
pub event: Event,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FromEventData {
|
||||||
|
unsafe fn from_event_data(x: &sys::VREvent_Data_t) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod event {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
/// Controller button events
|
||||||
|
pub struct Controller {
|
||||||
|
pub button: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromEventData for Controller {
|
||||||
|
unsafe fn from_event_data(x: &sys::VREvent_Data_t) -> Self {
|
||||||
|
let x = x.controller.as_ref();
|
||||||
|
Controller { button: x.button }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
/// Simulated mouse events in overlay space
|
||||||
|
pub struct Mouse {
|
||||||
|
/// Absolute position in texcoords, with the origin at the bottom left.
|
||||||
|
pub position: (f32, f32),
|
||||||
|
/// Bitfield
|
||||||
|
pub button: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromEventData for Mouse {
|
||||||
|
unsafe fn from_event_data(x: &sys::VREvent_Data_t) -> Self {
|
||||||
|
let x = x.mouse.as_ref();
|
||||||
|
Mouse { position: (x.x, x.y), button: x.button }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
/// Simulated mouse wheel scroll in overlay space
|
||||||
|
///
|
||||||
|
/// Coordinates are fraction of the touchpad traversed since last scroll event.
|
||||||
|
pub struct Scroll {
|
||||||
|
pub delta: (f32, f32),
|
||||||
|
pub repeat_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromEventData for Scroll {
|
||||||
|
unsafe fn from_event_data(x: &sys::VREvent_Data_t) -> Self {
|
||||||
|
let x = x.scroll.as_ref();
|
||||||
|
Scroll { delta: (x.xdelta, x.ydelta), repeat_count: x.repeatCount }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
/// When in mouse input mode you can receive data from the touchpad, these events are only sent if the user's finger
|
||||||
|
/// is on the touchpad (or just released from it)
|
||||||
|
pub struct TouchPadMove {
|
||||||
|
/// if the user's finger is detected on the touch pad
|
||||||
|
pub finger_down: bool,
|
||||||
|
/// How long the finger has been down in seconds
|
||||||
|
pub seconds_finger_down: f32,
|
||||||
|
/// Starting finger position (so you can do some basic swipe stuff)
|
||||||
|
pub first: (f32, f32),
|
||||||
|
/// This is the raw sampled coordinate without deadzoning
|
||||||
|
pub raw: (f32, f32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromEventData for TouchPadMove {
|
||||||
|
unsafe fn from_event_data(x: &sys::VREvent_Data_t) -> Self {
|
||||||
|
let x = x.touchPadMove.as_ref();
|
||||||
|
TouchPadMove { finger_down: x.bFingerDown, seconds_finger_down: x.flSecondsFingerDown,
|
||||||
|
first: (x.fValueXFirst, x.fValueYFirst),
|
||||||
|
raw: (x.fValueXRaw, x.fValueYRaw) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
/// notification related events. Details will still change at this point
|
||||||
|
pub struct Notification {
|
||||||
|
pub user_value: u64,
|
||||||
|
pub notification_id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Process {
|
||||||
|
pub pid: u32,
|
||||||
|
pub old_pid: u32,
|
||||||
|
pub forced: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromEventData for Process {
|
||||||
|
unsafe fn from_event_data(x: &sys::VREvent_Data_t) -> Self {
|
||||||
|
let x = x.process.as_ref();
|
||||||
|
Process { pid: x.pid, old_pid: x.oldPid, forced: x.bForced }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Overlay {
|
||||||
|
pub overlay_handle: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromEventData for Overlay {
|
||||||
|
unsafe fn from_event_data(x: &sys::VREvent_Data_t) -> Self {
|
||||||
|
let x = x.overlay.as_ref();
|
||||||
|
Overlay { overlay_handle: x.overlayHandle }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Status {
|
||||||
|
pub status_state: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Keyboard {
|
||||||
|
pub new_input: [u8; 8],
|
||||||
|
pub user_value: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromEventData for Keyboard {
|
||||||
|
unsafe fn from_event_data(x: &sys::VREvent_Data_t) -> Self {
|
||||||
|
let x = &*(x.keyboard.as_ref() as *const _ as *const sys::VREvent_Keyboard_t_real);
|
||||||
|
Keyboard { new_input: *(x.cNewInput.as_ptr() as *const _), user_value: x.uUserValue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Ipd {
|
||||||
|
pub ipd_meters: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Chaperone {
|
||||||
|
pub previous_universe: u64,
|
||||||
|
pub current_universe: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Property {
|
||||||
|
pub container: PropertyContainerHandle,
|
||||||
|
pub property: TrackedDeviceProperty,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromEventData for Property {
|
||||||
|
unsafe fn from_event_data(x: &sys::VREvent_Data_t) -> Self {
|
||||||
|
let x: &sys::VREvent_Property_t = &*(x as *const _ as *const _); // Field is missing from union
|
||||||
|
Property {
|
||||||
|
container: x.container,
|
||||||
|
property: x.prop,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum Event {
|
||||||
|
TrackedDeviceActivated,
|
||||||
|
TrackedDeviceDeactivated,
|
||||||
|
TrackedDeviceUpdated,
|
||||||
|
TrackedDeviceUserInteractionStarted,
|
||||||
|
TrackedDeviceUserInteractionEnded,
|
||||||
|
IpdChanged,
|
||||||
|
EnterStandbyMode,
|
||||||
|
LeaveStandbyMode,
|
||||||
|
TrackedDeviceRoleChanged,
|
||||||
|
WatchdogWakeUpRequested,
|
||||||
|
LensDistortionChanged,
|
||||||
|
PropertyChanged(event::Property),
|
||||||
|
|
||||||
|
ButtonPress(event::Controller),
|
||||||
|
ButtonUnpress(event::Controller),
|
||||||
|
ButtonTouch(event::Controller),
|
||||||
|
ButtonUntouch(event::Controller),
|
||||||
|
|
||||||
|
MouseMove(event::Mouse),
|
||||||
|
MouseButtonDown(event::Mouse),
|
||||||
|
MouseButtonUp(event::Mouse),
|
||||||
|
FocusEnter(event::Overlay),
|
||||||
|
FocusLeave(event::Overlay),
|
||||||
|
Scroll(event::Scroll),
|
||||||
|
TouchPadMove(event::TouchPadMove),
|
||||||
|
/// global event
|
||||||
|
OverlayFocusChanged(event::Overlay),
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
InputFocusCaptured(event::Process),
|
||||||
|
#[deprecated]
|
||||||
|
InputFocusReleased(event::Process),
|
||||||
|
SceneFocusLost(event::Process),
|
||||||
|
SceneFocusGained(event::Process),
|
||||||
|
/// The app actually drawing the scene changed (usually to or from the compositor)
|
||||||
|
SceneApplicationChanged(event::Process),
|
||||||
|
/// New app got access to draw the scene
|
||||||
|
SceneFocusChanged(event::Process),
|
||||||
|
InputFocusChanged(event::Process),
|
||||||
|
SceneApplicationSecondaryRenderingStarted(event::Process),
|
||||||
|
|
||||||
|
/// Sent to the scene application to request hiding render models temporarily
|
||||||
|
HideRenderModels,
|
||||||
|
/// Sent to the scene application to request restoring render model visibility
|
||||||
|
ShowRenderModels,
|
||||||
|
|
||||||
|
OverlayShown,
|
||||||
|
OverlayHidden,
|
||||||
|
DashboardActivated,
|
||||||
|
DashboardDeactivated,
|
||||||
|
/// Sent to the overlay manager - data is overlay
|
||||||
|
DashboardThumbSelected,
|
||||||
|
/// Sent to the overlay manager - data is overlay
|
||||||
|
DashboardRequested,
|
||||||
|
/// Send to the overlay manager
|
||||||
|
ResetDashboard,
|
||||||
|
/// Send to the dashboard to render a toast - data is the notification ID
|
||||||
|
RenderToast,
|
||||||
|
/// Sent to overlays when a SetOverlayRaw or SetOverlayFromFile call finishes loading
|
||||||
|
ImageLoaded,
|
||||||
|
/// Sent to keyboard renderer in the dashboard to invoke it
|
||||||
|
ShowKeyboard,
|
||||||
|
/// Sent to keyboard renderer in the dashboard to hide it
|
||||||
|
HideKeyboard,
|
||||||
|
/// Sent to an overlay when IVROverlay::SetFocusOverlay is called on it
|
||||||
|
OverlayGamepadFocusGained,
|
||||||
|
/// Send to an overlay when it previously had focus and IVROverlay::SetFocusOverlay is called on something else
|
||||||
|
OverlayGamepadFocusLost,
|
||||||
|
OverlaySharedTextureChanged,
|
||||||
|
DashboardGuideButtonDown,
|
||||||
|
DashboardGuideButtonUp,
|
||||||
|
/// Screenshot button combo was pressed, Dashboard should request a screenshot
|
||||||
|
ScreenshotTriggered,
|
||||||
|
/// Sent to overlays when a SetOverlayRaw or SetOverlayfromFail fails to load
|
||||||
|
ImageFailed,
|
||||||
|
DashboardOverlayCreated,
|
||||||
|
|
||||||
|
/// Sent by vrclient application to compositor to take a screenshot
|
||||||
|
RequestScreenshot,
|
||||||
|
/// Sent by compositor to the application that the screenshot has been taken
|
||||||
|
ScreenshotTaken,
|
||||||
|
/// Sent by compositor to the application that the screenshot failed to be taken
|
||||||
|
ScreenshotFailed,
|
||||||
|
/// Sent by compositor to the dashboard that a completed screenshot was submitted
|
||||||
|
SubmitScreenshotToDashboard,
|
||||||
|
/// Sent by compositor to the dashboard that a completed screenshot was submitted
|
||||||
|
ScreenshotProgressToDashboard,
|
||||||
|
|
||||||
|
PrimaryDashboardDeviceChanged,
|
||||||
|
|
||||||
|
Notification_Shown,
|
||||||
|
Notification_Hidden,
|
||||||
|
Notification_BeginInteraction,
|
||||||
|
Notification_Destroyed,
|
||||||
|
|
||||||
|
Quit(event::Process),
|
||||||
|
ProcessQuit(event::Process),
|
||||||
|
QuitAborted_UserPrompt(event::Process),
|
||||||
|
QuitAcknowledged(event::Process),
|
||||||
|
/// The driver has requested that SteamVR shut down
|
||||||
|
DriverRequestedQuit,
|
||||||
|
|
||||||
|
ChaperoneDataHasChanged,
|
||||||
|
ChaperoneUniverseHasChanged,
|
||||||
|
ChaperoneTempDataHasChanged,
|
||||||
|
ChaperoneSettingsHaveChanged,
|
||||||
|
SeatedZeroPoseReset,
|
||||||
|
|
||||||
|
AudioSettingsHaveChanged,
|
||||||
|
|
||||||
|
BackgroundSettingHasChanged,
|
||||||
|
CameraSettingsHaveChanged,
|
||||||
|
ReprojectionSettingHasChanged,
|
||||||
|
ModelSkinSettingsHaveChanged,
|
||||||
|
EnvironmentSettingsHaveChanged,
|
||||||
|
PowerSettingsHaveChanged,
|
||||||
|
|
||||||
|
StatusUpdate,
|
||||||
|
|
||||||
|
MCImageUpdated,
|
||||||
|
|
||||||
|
FirmwareUpdateStarted,
|
||||||
|
FirmwareUpdateFinished,
|
||||||
|
|
||||||
|
KeyboardClosed,
|
||||||
|
KeyboardCharInput(event::Keyboard),
|
||||||
|
/// Sent when DONE button clicked on keyboard
|
||||||
|
KeyboardDone,
|
||||||
|
|
||||||
|
ApplicationTransitionStarted,
|
||||||
|
ApplicationTransitionAborted,
|
||||||
|
ApplicationTransitionNewAppStarted,
|
||||||
|
ApplicationListUpdated,
|
||||||
|
ApplicationMimeTypeLoad,
|
||||||
|
ApplicationTransitionNewAppLaunchComplete,
|
||||||
|
ProcessConnected,
|
||||||
|
ProcessDisconnected,
|
||||||
|
|
||||||
|
Compositor_MirrorWindowShown,
|
||||||
|
Compositor_MirrorWindowHidden,
|
||||||
|
Compositor_ChaperoneBoundsShown,
|
||||||
|
Compositor_ChaperoneBoundsHidden,
|
||||||
|
|
||||||
|
TrackedCamera_StartVideoStream,
|
||||||
|
TrackedCamera_StopVideoStream,
|
||||||
|
TrackedCamera_PauseVideoStream,
|
||||||
|
TrackedCamera_ResumeVideoStream,
|
||||||
|
TrackedCamera_EditingSurface,
|
||||||
|
|
||||||
|
PerformanceTest_EnableCapture,
|
||||||
|
PerformanceTest_DisableCapture,
|
||||||
|
PerformanceTest_FidelityLevel,
|
||||||
|
|
||||||
|
MessageOverlay_Closed,
|
||||||
|
|
||||||
|
VendorSpecific(u32),
|
||||||
|
Unknown(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Event {
|
||||||
|
fn from_sys(ty: u32, data: &sys::VREvent_Data_t) -> Self {
|
||||||
|
use self::Event::*;
|
||||||
|
|
||||||
|
fn get<T: FromEventData>(x: &sys::VREvent_Data_t) -> T {
|
||||||
|
unsafe { T::from_event_data(x) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
|
match ty {
|
||||||
|
sys::EVREventType_EVREventType_VREvent_TrackedDeviceActivated => TrackedDeviceActivated,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_TrackedDeviceDeactivated => TrackedDeviceDeactivated,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_TrackedDeviceUpdated => TrackedDeviceUpdated,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_TrackedDeviceUserInteractionStarted => TrackedDeviceUserInteractionStarted,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_TrackedDeviceUserInteractionEnded => TrackedDeviceUserInteractionEnded,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_IpdChanged => IpdChanged,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_EnterStandbyMode => EnterStandbyMode,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_LeaveStandbyMode => LeaveStandbyMode,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_TrackedDeviceRoleChanged => TrackedDeviceRoleChanged,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_WatchdogWakeUpRequested => WatchdogWakeUpRequested,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_LensDistortionChanged => LensDistortionChanged,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_PropertyChanged => PropertyChanged(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ButtonPress => ButtonPress(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ButtonUnpress => ButtonUnpress(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ButtonTouch => ButtonTouch(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ButtonUntouch => ButtonUntouch(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_MouseMove => MouseMove(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_MouseButtonDown => MouseButtonDown(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_MouseButtonUp => MouseButtonUp(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_FocusEnter => FocusEnter(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_FocusLeave => FocusLeave(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_Scroll => Scroll(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_TouchPadMove => TouchPadMove(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_OverlayFocusChanged => OverlayFocusChanged(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_InputFocusCaptured => InputFocusCaptured(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_InputFocusReleased => InputFocusReleased(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_SceneFocusLost => SceneFocusLost(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_SceneFocusGained => SceneFocusGained(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_SceneApplicationChanged => SceneApplicationChanged(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_SceneFocusChanged => SceneFocusChanged(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_InputFocusChanged => InputFocusChanged(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_SceneApplicationSecondaryRenderingStarted => SceneApplicationSecondaryRenderingStarted(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_HideRenderModels => HideRenderModels,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ShowRenderModels => ShowRenderModels,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_OverlayShown => OverlayShown,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_OverlayHidden => OverlayHidden,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_DashboardActivated => DashboardActivated,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_DashboardDeactivated => DashboardDeactivated,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_DashboardThumbSelected => DashboardThumbSelected,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_DashboardRequested => DashboardRequested,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ResetDashboard => ResetDashboard,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_RenderToast => RenderToast,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ImageLoaded => ImageLoaded,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ShowKeyboard => ShowKeyboard,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_HideKeyboard => HideKeyboard,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_OverlayGamepadFocusGained => OverlayGamepadFocusGained,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_OverlayGamepadFocusLost => OverlayGamepadFocusLost,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_OverlaySharedTextureChanged => OverlaySharedTextureChanged,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_DashboardGuideButtonDown => DashboardGuideButtonDown,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_DashboardGuideButtonUp => DashboardGuideButtonUp,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ScreenshotTriggered => ScreenshotTriggered,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ImageFailed => ImageFailed,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_DashboardOverlayCreated => DashboardOverlayCreated,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_RequestScreenshot => RequestScreenshot,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ScreenshotTaken => ScreenshotTaken,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ScreenshotFailed => ScreenshotFailed,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_SubmitScreenshotToDashboard => SubmitScreenshotToDashboard,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ScreenshotProgressToDashboard => ScreenshotProgressToDashboard,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_PrimaryDashboardDeviceChanged => PrimaryDashboardDeviceChanged,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_Notification_Shown => Notification_Shown,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_Notification_Hidden => Notification_Hidden,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_Notification_BeginInteraction => Notification_BeginInteraction,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_Notification_Destroyed => Notification_Destroyed,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_Quit => Quit(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ProcessQuit => ProcessQuit(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_QuitAborted_UserPrompt => QuitAborted_UserPrompt(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_QuitAcknowledged => QuitAcknowledged(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_DriverRequestedQuit => DriverRequestedQuit,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ChaperoneDataHasChanged => ChaperoneDataHasChanged,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ChaperoneUniverseHasChanged => ChaperoneUniverseHasChanged,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ChaperoneTempDataHasChanged => ChaperoneTempDataHasChanged,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ChaperoneSettingsHaveChanged => ChaperoneSettingsHaveChanged,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_SeatedZeroPoseReset => SeatedZeroPoseReset,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_AudioSettingsHaveChanged => AudioSettingsHaveChanged,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_BackgroundSettingHasChanged => BackgroundSettingHasChanged,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_CameraSettingsHaveChanged => CameraSettingsHaveChanged,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ReprojectionSettingHasChanged => ReprojectionSettingHasChanged,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ModelSkinSettingsHaveChanged => ModelSkinSettingsHaveChanged,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_EnvironmentSettingsHaveChanged => EnvironmentSettingsHaveChanged,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_PowerSettingsHaveChanged => PowerSettingsHaveChanged,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_StatusUpdate => StatusUpdate,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_MCImageUpdated => MCImageUpdated,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_FirmwareUpdateStarted => FirmwareUpdateStarted,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_FirmwareUpdateFinished => FirmwareUpdateFinished,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_KeyboardClosed => KeyboardClosed,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_KeyboardCharInput => KeyboardCharInput(get(data)),
|
||||||
|
sys::EVREventType_EVREventType_VREvent_KeyboardDone => KeyboardDone,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ApplicationTransitionStarted => ApplicationTransitionStarted,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ApplicationTransitionAborted => ApplicationTransitionAborted,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ApplicationTransitionNewAppStarted => ApplicationTransitionNewAppStarted,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ApplicationListUpdated => ApplicationListUpdated,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ApplicationMimeTypeLoad => ApplicationMimeTypeLoad,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ApplicationTransitionNewAppLaunchComplete => ApplicationTransitionNewAppLaunchComplete,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ProcessConnected => ProcessConnected,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_ProcessDisconnected => ProcessDisconnected,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_Compositor_MirrorWindowShown => Compositor_MirrorWindowShown,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_Compositor_MirrorWindowHidden => Compositor_MirrorWindowHidden,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_Compositor_ChaperoneBoundsShown => Compositor_ChaperoneBoundsShown,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_Compositor_ChaperoneBoundsHidden => Compositor_ChaperoneBoundsHidden,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_TrackedCamera_StartVideoStream => TrackedCamera_StartVideoStream,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_TrackedCamera_StopVideoStream => TrackedCamera_StopVideoStream,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_TrackedCamera_PauseVideoStream => TrackedCamera_PauseVideoStream,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_TrackedCamera_ResumeVideoStream => TrackedCamera_ResumeVideoStream,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_TrackedCamera_EditingSurface => TrackedCamera_EditingSurface,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_PerformanceTest_EnableCapture => PerformanceTest_EnableCapture,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_PerformanceTest_DisableCapture => PerformanceTest_DisableCapture,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_PerformanceTest_FidelityLevel => PerformanceTest_FidelityLevel,
|
||||||
|
sys::EVREventType_EVREventType_VREvent_MessageOverlay_Closed => MessageOverlay_Closed,
|
||||||
|
x if x >= sys::EVREventType_EVREventType_VREvent_VendorSpecific_Reserved_Start
|
||||||
|
&& x <= sys::EVREventType_EVREventType_VREvent_VendorSpecific_Reserved_End => VendorSpecific(x),
|
||||||
|
x => Unknown(x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type PropertyContainerHandle = sys::PropertyContainerHandle_t;
|
||||||
|
238
src/tracking.rs
238
src/tracking.rs
@ -1,212 +1,58 @@
|
|||||||
use openvr_sys;
|
use openvr_sys as sys;
|
||||||
use openvr_sys::ETrackedPropertyError::*;
|
|
||||||
|
|
||||||
use subsystems::*;
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
use error::*;
|
pub enum TrackingUniverseOrigin {
|
||||||
use std::slice;
|
Seated = sys::ETrackingUniverseOrigin_ETrackingUniverseOrigin_TrackingUniverseSeated as isize,
|
||||||
use std::str;
|
Standing = sys::ETrackingUniverseOrigin_ETrackingUniverseOrigin_TrackingUniverseStanding as isize,
|
||||||
|
RawAndUncalibrated = sys::ETrackingUniverseOrigin_ETrackingUniverseOrigin_TrackingUniverseRawAndUncalibrated as isize,
|
||||||
|
}
|
||||||
|
|
||||||
/// Describes a string property of a tracked device
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum TrackedDeviceStringProperty {
|
pub struct TrackedDevicePose(sys::TrackedDevicePose_t);
|
||||||
TrackingSystemName,
|
|
||||||
ModelNumber,
|
|
||||||
SerialNumber,
|
|
||||||
RenderModelName,
|
|
||||||
ManufacturerName,
|
|
||||||
TrackingFirmwareVersion,
|
|
||||||
HardwareRevision,
|
|
||||||
AllWirelessDongleDescriptions,
|
|
||||||
ConnectedWirelessDongle,
|
|
||||||
FirmwareManualUpdateURL,
|
|
||||||
FirmwareProgrammingTarget,
|
|
||||||
DisplayMCImageLeft,
|
|
||||||
DisplayMCImageRight,
|
|
||||||
DisplayGCImage,
|
|
||||||
CameraFirmwareDescription,
|
|
||||||
AttachedDeviceId,
|
|
||||||
ModeLabel
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TrackedDeviceStringProperty {
|
|
||||||
pub fn to_raw(&self) -> openvr_sys::ETrackedDeviceProperty {
|
|
||||||
use openvr_sys::ETrackedDeviceProperty::*;
|
|
||||||
use self::TrackedDeviceStringProperty::*;
|
|
||||||
|
|
||||||
match *self {
|
|
||||||
TrackingSystemName => ETrackedDeviceProperty_Prop_TrackingSystemName_String,
|
|
||||||
ModelNumber => ETrackedDeviceProperty_Prop_ModelNumber_String,
|
|
||||||
SerialNumber => ETrackedDeviceProperty_Prop_SerialNumber_String,
|
|
||||||
RenderModelName => ETrackedDeviceProperty_Prop_RenderModelName_String,
|
|
||||||
ManufacturerName => ETrackedDeviceProperty_Prop_ManufacturerName_String,
|
|
||||||
TrackingFirmwareVersion => ETrackedDeviceProperty_Prop_TrackingFirmwareVersion_String,
|
|
||||||
HardwareRevision => ETrackedDeviceProperty_Prop_HardwareRevision_String,
|
|
||||||
AllWirelessDongleDescriptions => ETrackedDeviceProperty_Prop_AllWirelessDongleDescriptions_String,
|
|
||||||
ConnectedWirelessDongle => ETrackedDeviceProperty_Prop_ConnectedWirelessDongle_String,
|
|
||||||
FirmwareManualUpdateURL => ETrackedDeviceProperty_Prop_Firmware_ManualUpdateURL_String,
|
|
||||||
FirmwareProgrammingTarget => ETrackedDeviceProperty_Prop_Firmware_ProgrammingTarget_String,
|
|
||||||
DisplayMCImageLeft => ETrackedDeviceProperty_Prop_DisplayMCImageLeft_String,
|
|
||||||
DisplayMCImageRight => ETrackedDeviceProperty_Prop_DisplayMCImageRight_String,
|
|
||||||
DisplayGCImage => ETrackedDeviceProperty_Prop_DisplayGCImage_String,
|
|
||||||
CameraFirmwareDescription => ETrackedDeviceProperty_Prop_CameraFirmwareDescription_String,
|
|
||||||
AttachedDeviceId => ETrackedDeviceProperty_Prop_AttachedDeviceId_String,
|
|
||||||
ModeLabel => ETrackedDeviceProperty_Prop_ModeLabel_String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Describes the class of a tracked device
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub enum TrackedDeviceClass {
|
|
||||||
Invalid,
|
|
||||||
HMD,
|
|
||||||
Controller,
|
|
||||||
TrackingReference,
|
|
||||||
Other,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TrackedDeviceClass {
|
|
||||||
pub fn to_raw(&self) -> openvr_sys::ETrackedDeviceClass {
|
|
||||||
use self::TrackedDeviceClass::*;
|
|
||||||
use openvr_sys::ETrackedDeviceClass::*;
|
|
||||||
|
|
||||||
match *self {
|
|
||||||
Invalid => ETrackedDeviceClass_TrackedDeviceClass_Invalid,
|
|
||||||
HMD => ETrackedDeviceClass_TrackedDeviceClass_HMD,
|
|
||||||
Controller => ETrackedDeviceClass_TrackedDeviceClass_Controller,
|
|
||||||
TrackingReference => ETrackedDeviceClass_TrackedDeviceClass_TrackingReference,
|
|
||||||
Other => ETrackedDeviceClass_TrackedDeviceClass_Other,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_raw(raw: openvr_sys::ETrackedDeviceClass) -> Self {
|
|
||||||
use self::TrackedDeviceClass::*;
|
|
||||||
use openvr_sys::ETrackedDeviceClass::*;
|
|
||||||
|
|
||||||
match raw {
|
|
||||||
ETrackedDeviceClass_TrackedDeviceClass_Invalid => Invalid,
|
|
||||||
ETrackedDeviceClass_TrackedDeviceClass_HMD => HMD,
|
|
||||||
ETrackedDeviceClass_TrackedDeviceClass_Controller => Controller,
|
|
||||||
ETrackedDeviceClass_TrackedDeviceClass_TrackingReference => TrackingReference,
|
|
||||||
ETrackedDeviceClass_TrackedDeviceClass_Other => Other,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct TrackedDevicePose {
|
|
||||||
pub index: usize,
|
|
||||||
pub to_device: [[f32; 4]; 3],
|
|
||||||
pub velocity: [f32; 3],
|
|
||||||
pub angular_velocity: [f32; 3],
|
|
||||||
pub is_valid: bool,
|
|
||||||
pub is_connected: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TrackedDevicePose {
|
impl TrackedDevicePose {
|
||||||
pub fn from_raw(i: usize, d: openvr_sys::TrackedDevicePose_t) -> Self {
|
pub fn device_to_absolute_tracking(&self) -> &[[f32; 4]; 3] { &self.0.mDeviceToAbsoluteTracking.m }
|
||||||
TrackedDevicePose {
|
pub fn velocity(&self) -> &[f32; 3] { &self.0.vVelocity.v }
|
||||||
index: i,
|
pub fn angular_velocity(&self) -> &[f32; 3] { &self.0.vAngularVelocity.v }
|
||||||
is_connected: d.bDeviceIsConnected > 0,
|
pub fn tracking_result(&self) -> TrackingResult {
|
||||||
is_valid: d.bPoseIsValid > 0,
|
use self::TrackingResult::*;
|
||||||
to_device: d.mDeviceToAbsoluteTracking.m,
|
match self.0.eTrackingResult {
|
||||||
velocity: d.vVelocity.v,
|
sys::ETrackingResult_ETrackingResult_TrackingResult_Uninitialized => Uninitialized,
|
||||||
angular_velocity: d.vAngularVelocity.v,
|
sys::ETrackingResult_ETrackingResult_TrackingResult_Calibrating_InProgress => CalibratingInProgress,
|
||||||
}
|
sys::ETrackingResult_ETrackingResult_TrackingResult_Calibrating_OutOfRange => CalibratingOutOfRange,
|
||||||
}
|
sys::ETrackingResult_ETrackingResult_TrackingResult_Running_OK => OK,
|
||||||
|
sys::ETrackingResult_ETrackingResult_TrackingResult_Running_OutOfRange => RunningOutOfRange,
|
||||||
// returns the device class of the tracked object
|
_ => panic!("unrecognized tracking result")
|
||||||
pub fn device_class(&self) -> TrackedDeviceClass {
|
|
||||||
unsafe {
|
|
||||||
let system = * { system().unwrap().0 as *mut openvr_sys::VR_IVRSystem_FnTable};
|
|
||||||
TrackedDeviceClass::from_raw(system.GetTrackedDeviceClass.unwrap()(self.index as u32))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// gets a propery as a string
|
|
||||||
pub fn get_property_string(&self, property: TrackedDeviceStringProperty) -> Result<String, Error<openvr_sys::ETrackedPropertyError>> {
|
|
||||||
unsafe {
|
|
||||||
let system = * { system().unwrap().0 as *mut openvr_sys::VR_IVRSystem_FnTable};
|
|
||||||
|
|
||||||
let val_out = String::with_capacity(256);
|
|
||||||
let mut err = ETrackedPropertyError_TrackedProp_Success;
|
|
||||||
|
|
||||||
let size = system.GetStringTrackedDeviceProperty.unwrap()(
|
|
||||||
self.index as u32,
|
|
||||||
property.to_raw(),
|
|
||||||
val_out.as_ptr() as *mut i8,
|
|
||||||
256,
|
|
||||||
&mut err
|
|
||||||
);
|
|
||||||
|
|
||||||
if size > 0 {
|
|
||||||
let ptr = val_out.as_ptr() as *mut u8;
|
|
||||||
let mem = slice::from_raw_parts(ptr, size as usize);
|
|
||||||
let str = str::from_utf8(mem).unwrap();
|
|
||||||
return Ok(String::from(str));
|
|
||||||
} else {
|
|
||||||
return Err(Error::from_raw(err));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn pose_is_valid(&self) -> bool { self.0.bPoseIsValid }
|
||||||
|
pub fn device_is_connected(&self) -> bool { self.0.bDeviceIsConnected }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum TrackingResult {
|
||||||
#[derive(Debug, Copy, Clone)]
|
Uninitialized = sys::ETrackingResult_ETrackingResult_TrackingResult_Uninitialized as isize,
|
||||||
pub struct TrackedDevicePoses {
|
CalibratingInProgress = sys::ETrackingResult_ETrackingResult_TrackingResult_Calibrating_InProgress as isize,
|
||||||
pub count: usize,
|
CalibratingOutOfRange = sys::ETrackingResult_ETrackingResult_TrackingResult_Calibrating_OutOfRange as isize,
|
||||||
pub poses: [TrackedDevicePose; 16],
|
OK = sys::ETrackingResult_ETrackingResult_TrackingResult_Running_OK as isize,
|
||||||
|
RunningOutOfRange = sys::ETrackingResult_ETrackingResult_TrackingResult_Running_OutOfRange as isize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TrackedDevicePosesIterator<'a> {
|
pub enum TrackedDeviceClass {
|
||||||
pub target: &'a TrackedDevicePoses,
|
Invalid = sys::ETrackedDeviceClass_ETrackedDeviceClass_TrackedDeviceClass_Invalid as isize,
|
||||||
pub index: usize
|
HMD = sys::ETrackedDeviceClass_ETrackedDeviceClass_TrackedDeviceClass_HMD as isize,
|
||||||
|
Controller = sys::ETrackedDeviceClass_ETrackedDeviceClass_TrackedDeviceClass_Controller as isize,
|
||||||
|
GenericTracker = sys::ETrackedDeviceClass_ETrackedDeviceClass_TrackedDeviceClass_GenericTracker as isize,
|
||||||
|
TrackingReference = sys::ETrackedDeviceClass_ETrackedDeviceClass_TrackedDeviceClass_TrackingReference as isize,
|
||||||
|
DisplayRedirect = sys::ETrackedDeviceClass_ETrackedDeviceClass_TrackedDeviceClass_DisplayRedirect as isize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TrackedDevicePoses {
|
pub type TrackedDeviceIndex = sys::TrackedDeviceIndex_t;
|
||||||
pub fn as_slice(&self) -> &[TrackedDevicePose] {
|
|
||||||
&self.poses[0..self.count]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// creates an iterator that will iterate over all connected devices
|
pub mod tracked_device_index {
|
||||||
pub fn connected_iter(&self) -> TrackedDevicePosesIterator {
|
use super::*;
|
||||||
TrackedDevicePosesIterator { target: self, index: 0 }
|
pub const HMD: TrackedDeviceIndex = sys::k_unTrackedDeviceIndex_Hmd;
|
||||||
}
|
pub const INVALID: TrackedDeviceIndex = sys::k_unTrackedDeviceIndexInvalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for TrackedDevicePosesIterator<'a> {
|
pub type TrackedDeviceProperty = sys::ETrackedDeviceProperty;
|
||||||
type Item = &'a TrackedDevicePose;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<&'a TrackedDevicePose> {
|
|
||||||
// end reached
|
|
||||||
if self.index == self.target.count {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let res = &self.target.poses[self.index];
|
|
||||||
if !res.is_valid || !res.is_connected {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.index += 1;
|
|
||||||
|
|
||||||
Some(res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn to_tracked(data: [openvr_sys::TrackedDevicePose_t; 16]) -> TrackedDevicePoses {
|
|
||||||
use std;
|
|
||||||
let mut out: TrackedDevicePoses = std::mem::zeroed();
|
|
||||||
for (i, d) in data.iter().enumerate() {
|
|
||||||
if d.bDeviceIsConnected > 0 {
|
|
||||||
out.count = i + 1;
|
|
||||||
}
|
|
||||||
out.poses[i].index = i;
|
|
||||||
out.poses[i].is_connected = d.bDeviceIsConnected > 0;
|
|
||||||
out.poses[i].is_valid = d.bPoseIsValid > 0;
|
|
||||||
out.poses[i].to_device = d.mDeviceToAbsoluteTracking.m;
|
|
||||||
out.poses[i].velocity = d.vVelocity.v;
|
|
||||||
out.poses[i].angular_velocity = d.vAngularVelocity.v;
|
|
||||||
}
|
|
||||||
out
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user