From 8894438fd8d281ddb7a0b7a748338eee8d893cd9 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Sat, 15 Jul 2017 16:12:21 -0700 Subject: [PATCH] Render model components --- src/compositor/mod.rs | 22 ++-------- src/lib.rs | 33 ++++++++++++++- src/render_models.rs | 96 +++++++++++++++++++++++++++++++++++++++++-- src/system/mod.rs | 28 +++++++++---- 4 files changed, 147 insertions(+), 32 deletions(-) diff --git a/src/compositor/mod.rs b/src/compositor/mod.rs index 7cd9301..1812c5c 100644 --- a/src/compositor/mod.rs +++ b/src/compositor/mod.rs @@ -21,28 +21,14 @@ use super::*; impl<'a> Compositor<'a> { pub fn vulkan_instance_extensions_required(&self) -> Vec { - let temp = unsafe { - let n = self.0.GetVulkanInstanceExtensionsRequired.unwrap()(ptr::null_mut(), 0); - let mut buffer: Vec = Vec::new(); - buffer.resize(n as usize, mem::uninitialized()); - (self.0.GetVulkanInstanceExtensionsRequired.unwrap())(buffer.as_mut_ptr() as *mut i8, n); - buffer.truncate((n-1) as usize); // Strip trailing null - buffer - }; - temp.split(|&x| x == b' ').map(|x| CString::new(x.to_vec()).expect("extension name contained null byte")).collect() + let temp = unsafe { get_string(|ptr, n| self.0.GetVulkanInstanceExtensionsRequired.unwrap()(ptr, n)) }.unwrap(); + temp.as_bytes().split(|&x| x == b' ').map(|x| CString::new(x.to_vec()).expect("extension name contained null byte")).collect() } /// Safety: physical_device must be a valid VkPhysicalDevice pub unsafe fn vulkan_device_extensions_required(&self, physical_device: *mut VkPhysicalDevice_T) -> Vec { - let temp = { - let n = self.0.GetVulkanDeviceExtensionsRequired.unwrap()(physical_device, ptr::null_mut(), 0); - let mut buffer: Vec = 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.truncate((n-1) as usize); // Strip trailing null - buffer - }; - temp.split(|&x| x == b' ').map(|x| CString::new(x.to_vec()).expect("extension name contained null byte")).collect() + let temp = get_string(|ptr, n| self.0.GetVulkanDeviceExtensionsRequired.unwrap()(physical_device, ptr, n)).unwrap(); + temp.as_bytes().split(|&x| x == b' ').map(|x| CString::new(x.to_vec()).expect("extension name contained null byte")).collect() } /// Sets tracking space returned by WaitGetPoses diff --git a/src/lib.rs b/src/lib.rs index 661b184..0ccb1ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ extern crate openvr_sys; use std::sync::atomic::{Ordering, AtomicBool, ATOMIC_BOOL_INIT}; -use std::{fmt, error}; -use std::ffi::CStr; +use std::{fmt, error, ptr, mem}; +use std::ffi::{CStr, CString}; use openvr_sys as sys; @@ -130,3 +130,32 @@ pub enum Eye { Left = sys::EVREye_Eye_Left as isize, Right = sys::EVREye_Eye_Right as isize, } + +/// Helper to call OpenVR functions that return strings +unsafe fn get_string u32>(mut f: F) -> Option { + let n = f(ptr::null_mut(), 0); + if n == 0 { return None } + let mut storage = Vec::new(); + storage.reserve_exact(n as usize); + storage.resize(n as usize, mem::uninitialized()); + let n_ = f(storage.as_mut_ptr() as *mut _, n); + assert!(n == n_); + storage.truncate((n-1) as usize); // Strip trailing null + Some(CString::from_vec_unchecked(storage)) +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ControllerAxis { + pub x: f32, + pub y: f32, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ControllerState { + pub packet_num: u32, + pub button_pressed: u64, + pub button_touched: u64, + pub axis: [ControllerAxis; 5], +} diff --git a/src/render_models.rs b/src/render_models.rs index d42e934..855c5dc 100644 --- a/src/render_models.rs +++ b/src/render_models.rs @@ -1,9 +1,9 @@ -use std::{fmt, ptr, slice}; -use std::ffi::CStr; +use std::{fmt, ptr, slice, mem}; +use std::ffi::{CStr, CString}; use openvr_sys as sys; -use {RenderModels}; +use {RenderModels, ControllerState, get_string}; impl<'a> RenderModels<'a> { /// Loads and returns a render model for use in the application. `name` should be a render model name from the @@ -14,7 +14,7 @@ impl<'a> RenderModels<'a> { pub fn load_render_model(&self, name: &CStr) -> Result> { let mut ptr = ptr::null_mut(); let r = unsafe { - self.0.LoadRenderModel_Async.unwrap()(name as *const _ as *mut _, &mut ptr) + self.0.LoadRenderModel_Async.unwrap()(name.as_ptr() as *mut _, &mut ptr) }; match Error(r) { error::NONE => Ok(Some(Model { ptr: ptr, sys: self.0 })), @@ -23,6 +23,64 @@ impl<'a> RenderModels<'a> { } } + /// Returns the number of components of the specified render model. + /// + /// Components are useful when client application wish to draw, label, or otherwise interact with components of tracked objects. + /// Examples controller components: + /// renderable things such as triggers, buttons + /// non-renderable things which include coordinate systems such as 'tip', 'base', a neutral controller agnostic hand-pose + /// If all controller components are enumerated and rendered, it will be equivalent to drawing the traditional render model + /// Returns 0 if components not supported, >0 otherwise + pub fn component_count(&self, model: &CStr) -> u32 { + unsafe { self.0.GetComponentCount.unwrap()(model.as_ptr() as *mut _) } + } + + /// Get the names of available components. + /// + /// `component` does not correlate to a tracked device index, but is only used for iterating over all available + /// components. If it's out of range, this function will return None. Otherwise, it will return the size of the + /// buffer required for the name. + pub fn component_name(&self, model: &CStr, component: u32) -> Option { + unsafe { get_string(|ptr, n| self.0.GetComponentName.unwrap()(model.as_ptr() as *mut _, component, ptr, n)) } + } + + /// Gets all component names of a given model + pub fn component_names(&self, model: &CStr) -> ::std::vec::IntoIter { // FIXME: impl Iterator rather than allocating + let n = self.component_count(model); + (0..n).map(|i| self.component_name(model, i).expect("inconsistent component presence reported by OpenVR")).collect::>().into_iter() + } + + /// Use this to get the render model name for the specified rendermode/component combination, to be passed to + /// `load_render_model`. + /// + /// If the component name is out of range, this function will return None. + /// Otherwise, it will return the size of the buffer required for the name. + pub fn component_render_model_name(&self, model: &CStr, component: &CStr) -> Option { + unsafe { + get_string(|ptr, n| self.0.GetComponentRenderModelName.unwrap()( + model.as_ptr() as *mut _, component.as_ptr() as *mut _, ptr, n)) + } + } + + /// Use this to query information about the component, as a function of the controller state. + /// + /// Returns None if the component is invalid or should not be rendered in the current state. + /// + /// For dynamic controller components (ex: trigger) values will reflect component motions + /// For static components this will return a consistent value independent of the VRControllerState_t + pub fn component_state(&self, model: &CStr, component: &CStr, state: &ControllerState, mode: &ControllerMode) -> Option { + unsafe { + let mut out = mem::uninitialized(); + if self.0.GetComponentState.unwrap()(model.as_ptr() as *mut _, component.as_ptr() as *mut _, + state as *const _ as *mut _, mode as *const _ as *mut _, + &mut out as *mut _ as *mut _) { + Some(out) + } else { + None + } + } + } + /// Loads and returns a texture for use in the application. Texture IDs can be obtained from /// `Model::diffuse_texture_id()`. /// @@ -162,3 +220,33 @@ pub struct Vertex { pub normal: [f32; 3], pub texture_coord: [f32; 2], } + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ControllerMode { + pub scroll_wheel_visible: bool, +} + +impl Default for ControllerMode { + fn default() -> Self { ControllerMode { scroll_wheel_visible: false } } +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ComponentState { + pub tracking_to_component_render_model: [[f32; 4]; 3], + pub tracking_to_component_local: [[f32; 4]; 3], + pub properties: ComponentProperties, +} + +type ComponentProperties = sys::VRComponentProperties; + +pub mod component_properties { + use super::{sys, ComponentProperties}; + + pub const IS_STATIC: ComponentProperties = sys::EVRComponentProperty_VRComponentProperty_IsStatic; + pub const IS_VISIBLE: ComponentProperties = sys::EVRComponentProperty_VRComponentProperty_IsVisible; + pub const IS_TOUCHED: ComponentProperties = sys::EVRComponentProperty_VRComponentProperty_IsTouched; + pub const IS_PRESSED: ComponentProperties = sys::EVRComponentProperty_VRComponentProperty_IsPressed; + pub const IS_SCROLLED: ComponentProperties = sys::EVRComponentProperty_VRComponentProperty_IsScrolled; +} diff --git a/src/system/mod.rs b/src/system/mod.rs index 413fe0d..b0acfab 100644 --- a/src/system/mod.rs +++ b/src/system/mod.rs @@ -1,7 +1,7 @@ //! The `System` interface provides access to display configuration information, tracking data, controller state, //! events, and device properties. It is the main interface of OpenVR. -use std::{mem, ptr}; +use std::mem; use std::ffi::CString; use openvr_sys as sys; @@ -205,13 +205,25 @@ impl<'a> System<'a> { pub fn string_tracked_device_property(&self, device: TrackedDeviceIndex, property: TrackedDeviceProperty) -> Result { unsafe { let mut error = mem::uninitialized(); - let n = self.0.GetStringTrackedDeviceProperty.unwrap()(device, property, ptr::null_mut(), 0, &mut error); - if n == 0 { return Err(TrackedPropertyError(error)); } - let mut storage = Vec::new(); - storage.reserve_exact(n as usize); - storage.resize(n as usize, mem::uninitialized()); - self.0.GetStringTrackedDeviceProperty.unwrap()(device, property, storage.as_mut_ptr() as *mut i8, n, ptr::null_mut()); - Ok(CString::from_vec_unchecked(storage)) + let res = get_string(|ptr, n| self.0.GetStringTrackedDeviceProperty.unwrap()(device, property, ptr, n, &mut error)); + res.map_or(Err(TrackedPropertyError(error)), Ok) + } + } + + /// Looks up the current input state of a controller. + /// + /// Returns None if the device is not a controller, or if the user is currently in the system menu. + /// + /// Needed for rendering controller components (e.g. trigger) accurately wrt. user input using the `render_models` + /// API. + pub fn controller_state(&self, device: TrackedDeviceIndex) -> Option { + unsafe { + let mut state = mem::uninitialized(); + if self.0.GetControllerState.unwrap()(device, &mut state as *mut _ as *mut _, mem::size_of_val(&state) as u32) { + Some(state) + } else { + None + } } } }