use std::ffi::{CStr, CString};
use std::{fmt, mem, ptr, slice};
use openvr_sys as sys;
use crate::{get_string, ControllerState, RenderModels};
impl RenderModels {
pub fn load_render_model(&self, name: &CStr) -> Result<Option<Model>> {
let mut ptr = ptr::null_mut();
let r = unsafe { 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,
})),
error::LOADING => Ok(None),
x => Err(x),
}
}
pub fn component_count(&self, model: &CStr) -> u32 {
unsafe { self.0.GetComponentCount.unwrap()(model.as_ptr() as *mut _) }
}
pub fn component_name(&self, model: &CStr, component: u32) -> Option<CString> {
unsafe {
get_string(|ptr, n| {
self.0.GetComponentName.unwrap()(model.as_ptr() as *mut _, component, ptr, n)
})
}
}
pub fn component_names(&self, model: &CStr) -> ::std::vec::IntoIter<CString> {
let n = self.component_count(model);
(0..n)
.map(|i| {
self.component_name(model, i)
.expect("inconsistent component presence reported by OpenVR")
})
.collect::<Vec<_>>()
.into_iter()
}
pub fn component_render_model_name(&self, model: &CStr, component: &CStr) -> Option<CString> {
unsafe {
get_string(|ptr, n| {
self.0.GetComponentRenderModelName.unwrap()(
model.as_ptr() as *mut _,
component.as_ptr() as *mut _,
ptr,
n,
)
})
}
}
pub fn component_state(
&self,
model: &CStr,
component: &CStr,
state: &ControllerState,
mode: &ControllerMode,
) -> Option<ComponentState> {
unsafe {
let mut out = mem::MaybeUninit::uninit();
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 _,
out.as_mut_ptr() as *mut _,
) {
Some(out.assume_init())
} else {
None
}
}
}
pub fn load_texture(&self, id: TextureId) -> Result<Option<Texture>> {
let mut ptr = ptr::null_mut();
let r = unsafe { self.0.LoadTexture_Async.unwrap()(id.0, &mut ptr) };
match Error(r) {
error::NONE => Ok(Some(Texture {
ptr: ptr,
sys: self.0,
})),
error::LOADING => Ok(None),
x => Err(x),
}
}
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Error(sys::EVRRenderModelError);
pub mod error {
use super::{sys, Error};
pub const NONE: Error = Error(sys::EVRRenderModelError_VRRenderModelError_None);
pub const LOADING: Error = Error(sys::EVRRenderModelError_VRRenderModelError_Loading);
pub const NOT_SUPPORTED: Error =
Error(sys::EVRRenderModelError_VRRenderModelError_NotSupported);
pub const INVALID_ARG: Error = Error(sys::EVRRenderModelError_VRRenderModelError_InvalidArg);
pub const INVALID_MODEL: Error =
Error(sys::EVRRenderModelError_VRRenderModelError_InvalidModel);
pub const NO_SHAPES: Error = Error(sys::EVRRenderModelError_VRRenderModelError_NoShapes);
pub const MULTIPLE_SHAPES: Error =
Error(sys::EVRRenderModelError_VRRenderModelError_MultipleShapes);
pub const TOO_MANY_VERTICES: Error =
Error(sys::EVRRenderModelError_VRRenderModelError_TooManyVertices);
pub const MULTIPLE_TEXTURES: Error =
Error(sys::EVRRenderModelError_VRRenderModelError_MultipleTextures);
pub const BUFFER_TOO_SMALL: Error =
Error(sys::EVRRenderModelError_VRRenderModelError_BufferTooSmall);
pub const NOT_ENOUGH_NORMALS: Error =
Error(sys::EVRRenderModelError_VRRenderModelError_NotEnoughNormals);
pub const NOT_ENOUGH_TEX_COORDS: Error =
Error(sys::EVRRenderModelError_VRRenderModelError_NotEnoughTexCoords);
pub const INVALID_TEXTURE: Error =
Error(sys::EVRRenderModelError_VRRenderModelError_InvalidTexture);
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
}
}
impl ::std::error::Error for Error {
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::error::*;
let description = match *self {
NONE => "NONE",
LOADING => "LOADING",
NOT_SUPPORTED => "NOT_SUPPORTED",
INVALID_ARG => "INVALID_ARG",
INVALID_MODEL => "INVALID_MODEL",
NO_SHAPES => "NO_SHAPES",
MULTIPLE_SHAPES => "MULTIPLE_SHAPES",
TOO_MANY_VERTICES => "TOO_MANY_VERTICES",
MULTIPLE_TEXTURES => "MULTIPLE_TEXTURES",
BUFFER_TOO_SMALL => "BUFFER_TOO_SMALL",
NOT_ENOUGH_NORMALS => "NOT_ENOUGH_NORMALS",
NOT_ENOUGH_TEX_COORDS => "NOT_ENOUGH_TEX_COORDS",
INVALID_TEXTURE => "INVALID_TEXTURE",
_ => "UNKNOWN",
};
f.pad(description)
}
}
pub type Result<T> = ::std::result::Result<T, Error>;
pub struct Model<'a> {
ptr: *mut sys::RenderModel_t,
sys: &'a sys::VR_IVRRenderModels_FnTable,
}
impl<'a> Model<'a> {
pub fn vertices(&self) -> &[Vertex] {
unsafe {
let model = &*self.ptr;
slice::from_raw_parts(
model.rVertexData as *mut Vertex,
model.unVertexCount as usize,
)
}
}
pub fn indices(&self) -> &[u16] {
unsafe {
let model = &*self.ptr;
slice::from_raw_parts(model.rIndexData, 3 * model.unTriangleCount as usize)
}
}
pub fn diffuse_texture_id(&self) -> Option<TextureId> {
let id = unsafe { (&*self.ptr).diffuseTextureId };
if id < 0 {
None
} else {
Some(TextureId(id))
}
}
}
impl<'a> Drop for Model<'a> {
fn drop(&mut self) {
unsafe { self.sys.FreeRenderModel.unwrap()(self.ptr) }
}
}
pub struct Texture<'a> {
ptr: *mut sys::RenderModel_TextureMap_t,
sys: &'a sys::VR_IVRRenderModels_FnTable,
}
impl<'a> Texture<'a> {
pub fn dimensions(&self) -> (u16, u16) {
let tex = unsafe { &*self.ptr };
(tex.unWidth, tex.unHeight)
}
pub fn data(&self) -> &[u8] {
unsafe {
let tex = &*self.ptr;
slice::from_raw_parts(
tex.rubTextureMapData,
tex.unWidth as usize * tex.unHeight as usize * 4,
)
}
}
}
impl<'a> Drop for Texture<'a> {
fn drop(&mut self) {
unsafe { self.sys.FreeTexture.unwrap()(self.ptr) }
}
}
pub struct TextureId(pub sys::TextureID_t);
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Vertex {
pub position: [f32; 3],
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,
}
impl ComponentState {
pub fn is_static(&self) -> bool {
self.properties & component_properties::IS_STATIC != 0
}
pub fn is_visible(&self) -> bool {
self.properties & component_properties::IS_VISIBLE != 0
}
pub fn is_touched(&self) -> bool {
self.properties & component_properties::IS_TOUCHED != 0
}
pub fn is_pressed(&self) -> bool {
self.properties & component_properties::IS_PRESSED != 0
}
pub fn is_scrolled(&self) -> bool {
self.properties & component_properties::IS_SCROLLED != 0
}
}
type ComponentProperties = sys::VRComponentProperties;
pub mod component_properties {
use super::{sys, ComponentProperties};
pub const IS_STATIC: ComponentProperties =
sys::EVRComponentProperty_VRComponentProperty_IsStatic as ComponentProperties;
pub const IS_VISIBLE: ComponentProperties =
sys::EVRComponentProperty_VRComponentProperty_IsVisible as ComponentProperties;
pub const IS_TOUCHED: ComponentProperties =
sys::EVRComponentProperty_VRComponentProperty_IsTouched as ComponentProperties;
pub const IS_PRESSED: ComponentProperties =
sys::EVRComponentProperty_VRComponentProperty_IsPressed as ComponentProperties;
pub const IS_SCROLLED: ComponentProperties =
sys::EVRComponentProperty_VRComponentProperty_IsScrolled as ComponentProperties;
}
pub mod component {
pub mod controller {
use openvr_sys as sys;
use std::ffi::CStr;
lazy_static! {
pub static ref GDC2015: &'static CStr = unsafe {
CStr::from_bytes_with_nul_unchecked(sys::k_pch_Controller_Component_GDC2015)
};
pub static ref BASE: &'static CStr = unsafe {
CStr::from_bytes_with_nul_unchecked(sys::k_pch_Controller_Component_Base)
};
pub static ref TIP: &'static CStr =
unsafe { CStr::from_bytes_with_nul_unchecked(sys::k_pch_Controller_Component_Tip) };
pub static ref HAND_GRIP: &'static CStr = unsafe {
CStr::from_bytes_with_nul_unchecked(sys::k_pch_Controller_Component_HandGrip)
};
pub static ref STATUS: &'static CStr = unsafe {
CStr::from_bytes_with_nul_unchecked(sys::k_pch_Controller_Component_Status)
};
}
}
}