From 2695258ab2aa4fcd7e6d8ec9240de5c3bfcb3353 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Thu, 6 Jul 2017 22:51:28 -0700 Subject: [PATCH] Render models --- src/lib.rs | 2 + src/render_models.rs | 369 ++++++++++++++++++------------------------- src/tracking.rs | 4 +- 3 files changed, 159 insertions(+), 216 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4e7c967..ea0ebbe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,8 @@ mod tracking; pub mod system; pub mod compositor; +pub mod render_models; +pub mod property; pub use tracking::*; diff --git a/src/render_models.rs b/src/render_models.rs index 4ea4fd8..ce9b507 100644 --- a/src/render_models.rs +++ b/src/render_models.rs @@ -1,225 +1,164 @@ -use openvr_sys; -use openvr_sys::EVRRenderModelError::*; +use std::{fmt, ptr, slice}; +use std::ffi::CStr; -use std::string::String; -use std::ptr::null_mut; -use std::slice; -use subsystems::render_models; -use error::*; +use openvr_sys as sys; -pub struct IVRRenderModels(pub *const ()); +use {RenderModels}; -pub struct RenderModel(*mut openvr_sys::RenderModel_t); -pub struct RenderModelTexture(*mut openvr_sys::RenderModel_TextureMap_t); - -trait AsyncError { - /// checks if result is currently loading - fn is_loading(&self) -> bool; -} - -impl AsyncError for Error { - fn is_loading(&self) -> bool { - match self.to_raw() { - EVRRenderModelError_VRRenderModelError_Loading => { - true - }, - _ => { - false - } - } - } -} - -impl Drop for RenderModel { - /// will inform openvr that the memory for the render model is no longer required - fn drop (&mut self) { - unsafe { - let models = * { render_models().unwrap().0 as *mut openvr_sys::VR_IVRRenderModels_FnTable}; - models.FreeRenderModel.unwrap()( - self.0 - ); - } - } -} - -impl Drop for RenderModelTexture { - /// will inform openvr that the memory for the render model is no longer required - fn drop (&mut self) { - unsafe { - let models = * { render_models().unwrap().0 as *mut openvr_sys::VR_IVRRenderModels_FnTable}; - models.FreeTexture.unwrap()( - self.0 - ); - } - } -} - -impl RenderModel { - /// Returns an iterator that iterates over vertices - pub fn vertex_iter(&self) -> slice::Iter { - unsafe { - let slice = slice::from_raw_parts((*self.0).rVertexData, (*self.0).unVertexCount as usize); - slice.iter() - } - } - - /// Returns an iterator that iterates over indices - pub fn index_iter(&self) -> slice::Iter { - unsafe { - let slice = slice::from_raw_parts((*self.0).rIndexData, (*self.0).unTriangleCount as usize * 3); - slice.iter() - } - } - - /// asynchronosly loads the texture for the current render model - /// see IVRRenderModels::load_async for info how openvr async work - pub fn load_texture_async(&self) -> Result> { - unsafe { - let models = * { render_models().unwrap().0 as *mut openvr_sys::VR_IVRRenderModels_FnTable}; - let mut resp: *mut openvr_sys::RenderModel_TextureMap_t = null_mut(); - - let err = models.LoadTexture_Async.unwrap()( - (*self.0).diffuseTextureId, - &mut resp - ); - - match err { - EVRRenderModelError_VRRenderModelError_None => { - Ok(RenderModelTexture (resp)) - }, - _ => { - Err(Error::from_raw(err)) - } - } - - } - } - - /// loads the texture for current model - pub fn load_texture(&self) -> Result> { - use std; - - loop { - let result = self.load_texture_async(); - match result { - Ok(texture) => { - return Ok(texture); - }, - Err(err) => { - if !err.is_loading() { - return Err(err); - } - } - } - std::thread::sleep(std::time::Duration::from_millis(10)); - } - } -} - -impl RenderModelTexture { - /// Returns the dimension from the texture (width, height) - pub fn dimension(&self) -> (usize, usize) { - unsafe { - ((*self.0).unWidth as usize, (*self.0).unHeight as usize) - } - } - - /// Creates a 1 dimensional vector of pixels, format: rgba@32 - pub fn to_vec(&self) -> Vec { - unsafe { - let dimension = self.dimension(); - let slice = slice::from_raw_parts((*self.0).rubTextureMapData, dimension.0 * dimension.1 * 4); - let mut vec = Vec::new(); - vec.extend_from_slice(slice); - vec - } - } -} - -impl IVRRenderModels { - pub unsafe fn from_raw(ptr: *const ()) -> Self { - IVRRenderModels(ptr as *mut ()) - } - - /// Returns the amount of render models available - pub fn get_count(&self) -> u32 { - unsafe { - let models = * { self.0 as *mut openvr_sys::VR_IVRRenderModels_FnTable}; - - models.GetRenderModelCount.unwrap()() - } - } - - /// Returns the name of an available render model - pub fn get_name(&self, index: u32) -> String { - unsafe { - let models = * { self.0 as *mut openvr_sys::VR_IVRRenderModels_FnTable}; - let name_out = String::with_capacity(256); - - let size = models.GetRenderModelName.unwrap()( - index, - name_out.as_ptr() as *mut i8, - 256 - ); - - if size > 0 { - return String::from_raw_parts(name_out.as_ptr() as *mut _, (size - 1) as usize, (size - 1) as usize); - } else { - return String::from(""); - } +impl<'a> RenderModels<'a> { + /// Loads and returns a render model for use in the application. `name` should be a render model name from the + /// `RenderModelName_String` property or an absolute path name to a render model on disk. + /// + /// The method returns `Ok(None)` while the render model is still being loaded. Call it at regular intervals until + /// it returns `Ok(Some(model))`. + 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) }; - } - - /// Loads an render model into local memory - /// blocks the thread and waits until driver responds with model - pub fn load(&self, name: String) -> Result> { - use std; - - loop { - let result = self.load_async(name.clone()); - match result { - Ok(model) => { - return Ok(model); - }, - Err(err) => { - if !err.is_loading() { - return Err(err); - } - } - } - std::thread::sleep(std::time::Duration::from_millis(10)); + match Error(r) { + error::NONE => Ok(Some(Model { ptr: ptr, sys: self.0 })), + error::LOADING => Ok(None), + x => Err(x), } } - /// Loads an render model into local memory - /// When called for the first time openvr will start to load the model into memory - /// In the mean time this call will respond with EVRRenderModelError_VRRenderModelError_Loading - /// It is designed to be used wihtin the render loop as it won't block the user, for sync usage use load() - pub fn load_async(&self, name: String) -> Result> { - use std; - - unsafe { - let models = * { self.0 as *mut openvr_sys::VR_IVRRenderModels_FnTable}; - let mut resp: *mut openvr_sys::RenderModel_t = null_mut(); - let cname = std::ffi::CString::new(name.as_str()).unwrap(); - let rawname = cname.into_raw(); - - let err = models.LoadRenderModel_Async.unwrap()( - rawname, - &mut resp - ); - - let _ = std::ffi::CString::from_raw(rawname); - - match err { - EVRRenderModelError_VRRenderModelError_None => { - Ok(RenderModel ( resp )) - }, - _ => { - Err(Error::from_raw(err)) - } - } - + /// Loads and returns a texture for use in the application. Texture IDs can be obtained from + /// `Model::diffuse_texture_id()`. + /// + /// The method returns `Ok(None)` while the texture is still being loaded. Call it at regular intervals until it + /// returns `Ok(Some(texture))`. + pub fn load_texture(&self, id: TextureId) -> Result> { + let mut ptr = ptr::null_mut(); + let r = unsafe { + self.0.LoadTexture_Async.unwrap()(id, &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_EVRRenderModelError_VRRenderModelError_None); + pub const LOADING: Error = Error(sys::EVRRenderModelError_EVRRenderModelError_VRRenderModelError_Loading); + pub const NOT_SUPPORTED: Error = Error(sys::EVRRenderModelError_EVRRenderModelError_VRRenderModelError_NotSupported); + pub const INVALID_ARG: Error = Error(sys::EVRRenderModelError_EVRRenderModelError_VRRenderModelError_InvalidArg); + pub const INVALID_MODEL: Error = Error(sys::EVRRenderModelError_EVRRenderModelError_VRRenderModelError_InvalidModel); + pub const NO_SHAPES: Error = Error(sys::EVRRenderModelError_EVRRenderModelError_VRRenderModelError_NoShapes); + pub const MULTIPLE_SHAPES: Error = Error(sys::EVRRenderModelError_EVRRenderModelError_VRRenderModelError_MultipleShapes); + pub const TOO_MANY_VERTICES: Error = Error(sys::EVRRenderModelError_EVRRenderModelError_VRRenderModelError_TooManyVertices); + pub const MULTIPLE_TEXTURES: Error = Error(sys::EVRRenderModelError_EVRRenderModelError_VRRenderModelError_MultipleTextures); + pub const BUFFER_TOO_SMALL: Error = Error(sys::EVRRenderModelError_EVRRenderModelError_VRRenderModelError_BufferTooSmall); + pub const NOT_ENOUGH_NORMALS: Error = Error(sys::EVRRenderModelError_EVRRenderModelError_VRRenderModelError_NotEnoughNormals); + pub const NOT_ENOUGH_TEX_COORDS: Error = Error(sys::EVRRenderModelError_EVRRenderModelError_VRRenderModelError_NotEnoughTexCoords); + pub const INVALID_TEXTURE: Error = Error(sys::EVRRenderModelError_EVRRenderModelError_VRRenderModelError_InvalidTexture); +} + +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad(::std::error::Error::description(self)) + } +} + +impl ::std::error::Error for Error { + fn description(&self) -> &str { + use self::error::*; + 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", + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad(::std::error::Error::description(self)) + } +} + +pub type Result = ::std::result::Result; + +/// 3D geometry for rendering as an indexed triangle list +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 { + let id = unsafe { (&*self.ptr).diffuseTextureId }; + if id < 0 { None } else { Some(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) + } + + /// R8G8B8A8 + 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 type TextureId = 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], +} diff --git a/src/tracking.rs b/src/tracking.rs index d6904e1..4bfdb8f 100644 --- a/src/tracking.rs +++ b/src/tracking.rs @@ -65,4 +65,6 @@ pub enum TrackedControllerRole { RightHand = sys::ETrackedControllerRole_ETrackedControllerRole_TrackedControllerRole_RightHand as isize, } -pub type TrackedDevicePoses = [TrackedDevicePose; sys::k_unMaxTrackedDeviceCount as usize]; +pub const MAX_TRACKED_DEVICE_COUNT: usize = sys::k_unMaxTrackedDeviceCount as usize; + +pub type TrackedDevicePoses = [TrackedDevicePose; MAX_TRACKED_DEVICE_COUNT];