173 lines
6.0 KiB
Rust
173 lines
6.0 KiB
Rust
extern crate gfx_device_gl;
|
|
extern crate openvr as vr;
|
|
|
|
use std::fmt::Debug;
|
|
|
|
pub use self::vr::{compositor::texture::{ColorSpace, Handle, Texture},
|
|
ControllerState,
|
|
Eye,
|
|
system::Event as OvrEvent};
|
|
|
|
use gfx::{self, texture, Factory};
|
|
use gfx::memory::Typed;
|
|
use na;
|
|
|
|
use self::gfx_device_gl::Resources as GLResources;
|
|
|
|
pub struct VR {
|
|
_context: vr::Context,
|
|
system: vr::System,
|
|
compositor: vr::Compositor,
|
|
origin: vr::TrackingUniverseOrigin,
|
|
gfx_handles: gfx::handle::Manager<GLResources>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum Event {
|
|
Touch { dev_idx: u32, button: u32 },
|
|
Press { dev_idx: u32, button: u32 },
|
|
Unpress { dev_idx: u32, button: u32 },
|
|
Untouch { dev_idx: u32, button: u32 },
|
|
Other { dev_idx: u32, event: OvrEvent },
|
|
}
|
|
|
|
pub use self::vr::button_id;
|
|
|
|
#[derive(Debug)]
|
|
pub enum Error {
|
|
Init(Box<dyn Debug>),
|
|
Poses(Box<dyn Debug>),
|
|
Submit(Box<dyn Debug>)
|
|
}
|
|
|
|
impl From<vr::InitError> for Error {
|
|
fn from(e: vr::InitError) -> Self {
|
|
Error::Init(Box::new(e))
|
|
}
|
|
}
|
|
|
|
impl VR {
|
|
pub fn new() -> Result<VR, Error> {
|
|
let context = unsafe { vr::init(vr::ApplicationType::Scene)? };
|
|
Ok(VR {
|
|
system: context.system()?,
|
|
compositor: context.compositor()?,
|
|
_context: context,
|
|
origin: vr::TrackingUniverseOrigin::Standing,
|
|
gfx_handles: gfx::handle::Manager::new(),
|
|
})
|
|
}
|
|
|
|
pub fn poses(&mut self) -> Result<vr::TrackedDevicePoses, Error> {
|
|
self.gfx_handles.clear();
|
|
self.compositor.wait_get_poses().map(|p| p.render).map_err(|e| Error::Poses(Box::new(e)))
|
|
}
|
|
|
|
pub fn submit<T>(&mut self, eye: Eye, tex: &gfx::handle::Texture<GLResources, T>)
|
|
-> Result<(), Error> {
|
|
let tex_id = match self.gfx_handles.ref_texture(tex.raw()) {
|
|
&gfx_device_gl::NewTexture::Surface(id) => id,
|
|
_ => panic!("Not a surface")
|
|
};
|
|
unsafe {
|
|
self.compositor.submit(eye,
|
|
&Texture { handle: Handle::OpenGLRenderBuffer(tex_id as usize),
|
|
color_space: ColorSpace::Linear },
|
|
None,
|
|
None).map_err(|e| Error::Submit(Box::new(e)))
|
|
}
|
|
}
|
|
|
|
pub fn recommended_render_target_size(&self) -> (u32, u32) {
|
|
self.system.recommended_render_target_size()
|
|
}
|
|
|
|
pub fn projection_matrix(self: &Self, eye: Eye, near: f32, far: f32) -> na::Matrix4<f32> {
|
|
self.system.projection_matrix(eye, near, far).as_matrix4()
|
|
}
|
|
|
|
pub fn head_to_eye_transform(self: &Self, eye: Eye) -> na::Matrix4<f32> {
|
|
let mut mat = self.system.eye_to_head_transform(eye).as_matrix4();
|
|
let inv_worked = mat.try_inverse_mut();
|
|
assert!(inv_worked, "head_to_eye matrix invert");
|
|
mat
|
|
}
|
|
|
|
pub fn poll_next_event(&mut self) -> Option<Event> {
|
|
let (event, _) = self.system.poll_next_event_with_pose(self.origin)?;
|
|
|
|
let dev_idx = event.tracked_device_index as u32;
|
|
Some(match event.event {
|
|
OvrEvent::ButtonTouch(controller) => Event::Touch { dev_idx: dev_idx,
|
|
button: controller.button },
|
|
OvrEvent::ButtonPress(controller) => Event::Press { dev_idx: dev_idx,
|
|
button: controller.button },
|
|
OvrEvent::ButtonUnpress(controller) => Event::Unpress { dev_idx: dev_idx,
|
|
button: controller.button },
|
|
OvrEvent::ButtonUntouch(controller) => Event::Untouch { dev_idx: dev_idx,
|
|
button: controller.button },
|
|
_ => Event::Other { dev_idx: dev_idx, event: event.event },
|
|
})
|
|
}
|
|
|
|
pub fn get_controller_state(&self, index: u32) -> Option<ControllerState> {
|
|
self.system.controller_state(index)
|
|
}
|
|
}
|
|
|
|
pub trait AsMatrix4<N: na::Real> {
|
|
fn as_matrix4(self) -> na::Matrix4<N>;
|
|
}
|
|
impl<N: na::Real> AsMatrix4<N> for [[N; 4]; 3] {
|
|
#[inline]
|
|
fn as_matrix4(self) -> na::Matrix4<N> {
|
|
|
|
na::Matrix4::new(self[0][0], self[0][1], self[0][2], self[0][3],
|
|
self[1][0], self[1][1], self[1][2], self[1][3],
|
|
self[2][0], self[2][1], self[2][2], self[2][3],
|
|
N::zero(), N::zero(), N::zero(), N::one())
|
|
}
|
|
}
|
|
|
|
|
|
impl<N: na::Real> AsMatrix4<N> for [[N; 4]; 4] {
|
|
#[inline]
|
|
fn as_matrix4(self) -> na::Matrix4<N> {
|
|
|
|
na::Matrix4::new(self[0][0], self[0][1], self[0][2], self[0][3],
|
|
self[1][0], self[1][1], self[1][2], self[1][3],
|
|
self[2][0], self[2][1], self[2][2], self[2][3],
|
|
self[3][0], self[3][1], self[3][2], self[3][3])
|
|
}
|
|
}
|
|
|
|
|
|
pub struct EyeBuffer<T, D>
|
|
where T: gfx::format::RenderFormat + gfx::format::TextureFormat,
|
|
D: gfx::format::DepthFormat + gfx::format::TextureFormat {
|
|
|
|
pub tex: gfx::handle::Texture<GLResources, T::Surface>,
|
|
pub target: gfx::handle::RenderTargetView<GLResources, T>,
|
|
pub depth: gfx::handle::DepthStencilView<GLResources, D>,
|
|
}
|
|
|
|
pub fn create_eyebuffer<T, D>(factory: &mut gfx_device_gl::Factory,
|
|
width: u32,
|
|
height: u32)
|
|
-> Result<EyeBuffer<T, D>, gfx::CombinedError>
|
|
where T: gfx::format::RenderFormat + gfx::format::TextureFormat,
|
|
D: gfx::format::DepthFormat + gfx::format::TextureFormat {
|
|
let tex = factory
|
|
.create_texture(texture::Kind::D2(width as texture::Size,
|
|
height as texture::Size,
|
|
texture::AaMode::Single),
|
|
1, // levels
|
|
gfx::memory::Bind::RENDER_TARGET, // bind
|
|
gfx::memory::Usage::Data, // Usage
|
|
Some(<T::Channel as gfx::format::ChannelTyped>::get_channel_type()))?; // hint: format::ChannelType?
|
|
let tgt = factory.view_texture_as_render_target(&tex, 0, None)?;
|
|
let depth = factory.create_depth_stencil_view_only(width as texture::Size,
|
|
height as texture::Size)?;
|
|
Ok(EyeBuffer { tex: tex, target: tgt, depth: depth })
|
|
}
|