2019-04-10 22:10:17 -04:00
|
|
|
// pathfinder/demo/magicleap/src/magicleap.rs
|
2019-03-09 12:57:44 -05:00
|
|
|
//
|
|
|
|
// Copyright © 2019 The Pathfinder Project Developers.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
use crate::c_api::MLGraphicsBeginFrame;
|
|
|
|
use crate::c_api::MLGraphicsCreateClientGL;
|
|
|
|
use crate::c_api::MLGraphicsDestroyClient;
|
|
|
|
use crate::c_api::MLGraphicsEndFrame;
|
|
|
|
use crate::c_api::MLGraphicsGetRenderTargets;
|
|
|
|
use crate::c_api::MLGraphicsInitFrameParams;
|
|
|
|
use crate::c_api::MLGraphicsOptions;
|
|
|
|
use crate::c_api::MLGraphicsSignalSyncObjectGL;
|
|
|
|
use crate::c_api::MLGraphicsVirtualCameraInfoArray;
|
|
|
|
use crate::c_api::MLHandle;
|
|
|
|
use crate::c_api::MLHeadTrackingCreate;
|
|
|
|
use crate::c_api::MLLifecycleSetReadyIndication;
|
|
|
|
use crate::c_api::MLLogLevel;
|
|
|
|
use crate::c_api::MLLoggingLog;
|
|
|
|
use crate::c_api::MLMat4f;
|
|
|
|
use crate::c_api::MLQuaternionf;
|
|
|
|
use crate::c_api::MLRectf;
|
|
|
|
use crate::c_api::MLTransform;
|
|
|
|
use crate::c_api::MLVec3f;
|
|
|
|
use crate::c_api::ML_HANDLE_INVALID;
|
|
|
|
use crate::c_api::ML_RESULT_TIMEOUT;
|
|
|
|
use crate::c_api::ML_VIRTUAL_CAMERA_COUNT;
|
|
|
|
|
|
|
|
use egl;
|
|
|
|
use egl::EGL_NO_SURFACE;
|
|
|
|
use egl::EGLContext;
|
|
|
|
use egl::EGLDisplay;
|
|
|
|
|
|
|
|
use gl;
|
|
|
|
use gl::types::GLuint;
|
|
|
|
|
|
|
|
use log;
|
|
|
|
use log::debug;
|
|
|
|
use log::info;
|
|
|
|
|
|
|
|
use pathfinder_demo::window::CameraTransform;
|
|
|
|
use pathfinder_demo::window::Event;
|
2019-04-10 12:32:03 -04:00
|
|
|
use pathfinder_demo::window::View;
|
2019-03-09 12:57:44 -05:00
|
|
|
use pathfinder_demo::window::Window;
|
|
|
|
use pathfinder_demo::window::WindowSize;
|
|
|
|
use pathfinder_geometry::basic::point::Point2DI32;
|
|
|
|
use pathfinder_geometry::basic::point::Point2DF32;
|
|
|
|
use pathfinder_geometry::basic::rect::RectF32;
|
|
|
|
use pathfinder_geometry::basic::rect::RectI32;
|
|
|
|
use pathfinder_geometry::basic::transform3d::Perspective;
|
|
|
|
use pathfinder_geometry::basic::transform3d::Transform3DF32;
|
|
|
|
use pathfinder_geometry::distortion::BarrelDistortionCoefficients;
|
2019-03-27 17:40:09 -04:00
|
|
|
use pathfinder_geometry::util;
|
2019-03-09 12:57:44 -05:00
|
|
|
use pathfinder_gl::GLVersion;
|
|
|
|
use pathfinder_gpu::resources::FilesystemResourceLoader;
|
|
|
|
use pathfinder_gpu::resources::ResourceLoader;
|
|
|
|
use pathfinder_simd::default::F32x4;
|
|
|
|
|
2019-03-28 15:36:27 -04:00
|
|
|
use rayon::ThreadPoolBuilder;
|
|
|
|
|
2019-03-09 12:57:44 -05:00
|
|
|
use smallvec::SmallVec;
|
|
|
|
|
|
|
|
use std::ffi::CString;
|
|
|
|
use std::io::Write;
|
|
|
|
use std::mem;
|
|
|
|
use std::os::raw::c_void;
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use std::thread;
|
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
pub struct MagicLeapWindow {
|
|
|
|
framebuffer_id: GLuint,
|
|
|
|
graphics_client: MLHandle,
|
|
|
|
size: Point2DI32,
|
|
|
|
virtual_camera_array: MLGraphicsVirtualCameraInfoArray,
|
2019-03-28 12:22:22 -04:00
|
|
|
initial_camera_transform: Option<Transform3DF32>,
|
2019-03-09 12:57:44 -05:00
|
|
|
frame_handle: MLHandle,
|
|
|
|
resource_loader: FilesystemResourceLoader,
|
|
|
|
pose_event: Option<Vec<CameraTransform>>,
|
|
|
|
running: bool,
|
|
|
|
in_frame: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Window for MagicLeapWindow {
|
|
|
|
fn resource_loader(&self) -> &dyn ResourceLoader {
|
|
|
|
&self.resource_loader
|
|
|
|
}
|
|
|
|
|
|
|
|
fn gl_version(&self) -> GLVersion {
|
|
|
|
GLVersion::GL3
|
|
|
|
}
|
|
|
|
|
|
|
|
fn gl_default_framebuffer(&self) -> GLuint {
|
|
|
|
self.framebuffer_id
|
|
|
|
}
|
|
|
|
|
2019-04-09 15:35:52 -04:00
|
|
|
fn adjust_thread_pool_settings(&self, thread_pool_builder: ThreadPoolBuilder) -> ThreadPoolBuilder {
|
2019-03-28 15:36:27 -04:00
|
|
|
thread_pool_builder.start_handler(|id| unsafe { init_scene_thread(id) })
|
|
|
|
}
|
|
|
|
|
2019-03-09 12:57:44 -05:00
|
|
|
fn mouse_position(&self) -> Point2DI32 {
|
|
|
|
Point2DI32::new(0, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_user_event_id (&self) -> u32 {
|
|
|
|
0
|
|
|
|
}
|
|
|
|
|
|
|
|
fn push_user_event(_: u32, _: u32) {
|
|
|
|
}
|
|
|
|
|
|
|
|
fn present_open_svg_dialog(&mut self) {
|
|
|
|
}
|
|
|
|
|
|
|
|
fn run_save_dialog(&self, _: &str) -> Result<PathBuf, ()> {
|
|
|
|
Err(())
|
|
|
|
}
|
|
|
|
|
2019-04-10 12:32:03 -04:00
|
|
|
fn viewport(&self, _view: View) -> RectI32 {
|
|
|
|
RectI32::new(Point2DI32::default(), self.size)
|
2019-03-09 12:57:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn barrel_distortion_coefficients(&self) -> BarrelDistortionCoefficients {
|
|
|
|
BarrelDistortionCoefficients { k0: 0.0, k1: 0.0 }
|
|
|
|
}
|
|
|
|
|
2019-04-10 12:32:03 -04:00
|
|
|
fn make_current(&mut self, view: View) {
|
2019-03-09 12:57:44 -05:00
|
|
|
self.begin_frame();
|
2019-04-10 12:32:03 -04:00
|
|
|
let eye = match view {
|
|
|
|
View::Stereo(eye) if (eye as usize) < ML_VIRTUAL_CAMERA_COUNT => eye as usize,
|
|
|
|
_ => { debug!("Asked for unexpected view: {:?}", view); 0 }
|
2019-03-09 12:57:44 -05:00
|
|
|
};
|
|
|
|
debug!("Making {} current.", eye);
|
|
|
|
let viewport = self.virtual_camera_array.viewport;
|
|
|
|
let color_id = self.virtual_camera_array.color_id.as_gl_uint();
|
|
|
|
let depth_id = self.virtual_camera_array.depth_id.as_gl_uint();
|
|
|
|
let virtual_camera = self.virtual_camera_array.virtual_cameras[eye];
|
|
|
|
let layer_id = virtual_camera.virtual_camera_name as i32;
|
|
|
|
unsafe {
|
|
|
|
gl::FramebufferTextureLayer(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, color_id, 0, layer_id);
|
|
|
|
gl::FramebufferTextureLayer(gl::FRAMEBUFFER, gl::DEPTH_ATTACHMENT, depth_id, 0, layer_id);
|
|
|
|
gl::Viewport(viewport.x as i32, viewport.y as i32, viewport.w as i32, viewport.h as i32);
|
|
|
|
}
|
|
|
|
debug!("Made {} current.", eye);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn present(&mut self) {
|
|
|
|
self.end_frame();
|
|
|
|
self.begin_frame();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-28 15:36:27 -04:00
|
|
|
extern "C" {
|
|
|
|
fn init_scene_thread(id: usize);
|
|
|
|
}
|
|
|
|
|
2019-03-09 12:57:44 -05:00
|
|
|
fn get_proc_address(s: &str) -> *const c_void {
|
|
|
|
egl::get_proc_address(s) as *const c_void
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MagicLeapWindow {
|
|
|
|
pub fn new(egl_display: EGLDisplay, egl_context: EGLContext) -> MagicLeapWindow {
|
|
|
|
debug!("Creating MagicLeapWindow");
|
|
|
|
let mut framebuffer_id = 0;
|
|
|
|
let graphics_options = MLGraphicsOptions::default();
|
|
|
|
let mut graphics_client = unsafe { mem::zeroed() };
|
|
|
|
let mut head_tracker = unsafe { mem::zeroed() };
|
|
|
|
let mut targets = unsafe { mem::zeroed() };
|
|
|
|
let virtual_camera_array = unsafe { mem::zeroed() };
|
|
|
|
let handle = MLHandle::from(egl_context);
|
|
|
|
unsafe {
|
|
|
|
egl::make_current(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_context);
|
|
|
|
gl::load_with(get_proc_address);
|
|
|
|
gl::GenFramebuffers(1, &mut framebuffer_id);
|
|
|
|
MLGraphicsCreateClientGL(&graphics_options, handle, &mut graphics_client).unwrap();
|
|
|
|
MLLifecycleSetReadyIndication().unwrap();
|
|
|
|
MLHeadTrackingCreate(&mut head_tracker).unwrap();
|
|
|
|
MLGraphicsGetRenderTargets(graphics_client, &mut targets).unwrap();
|
|
|
|
}
|
|
|
|
let (max_width, max_height) = targets.buffers.iter().map(|buffer| buffer.color)
|
|
|
|
.chain(targets.buffers.iter().map(|buffer| buffer.depth))
|
|
|
|
.map(|target| (target.width as i32, target.height as i32))
|
|
|
|
.max()
|
|
|
|
.unwrap_or_default();
|
|
|
|
let resource_loader = FilesystemResourceLoader::locate();
|
|
|
|
debug!("Created MagicLeapWindow");
|
|
|
|
MagicLeapWindow {
|
|
|
|
framebuffer_id,
|
|
|
|
graphics_client,
|
|
|
|
size: Point2DI32::new(max_width, max_height),
|
|
|
|
frame_handle: ML_HANDLE_INVALID,
|
|
|
|
virtual_camera_array,
|
2019-03-28 12:22:22 -04:00
|
|
|
initial_camera_transform: None,
|
2019-03-09 12:57:44 -05:00
|
|
|
resource_loader,
|
|
|
|
pose_event: None,
|
|
|
|
running: true,
|
|
|
|
in_frame: false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn size(&self) -> WindowSize {
|
|
|
|
WindowSize {
|
|
|
|
logical_size: self.size,
|
|
|
|
backing_scale_factor: 1.0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn running(&self) -> bool {
|
|
|
|
self.running
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn try_get_event(&mut self) -> Option<Event> {
|
|
|
|
self.pose_event.take().map(Event::CameraTransforms)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn begin_frame(&mut self) {
|
|
|
|
if !self.in_frame {
|
2019-03-28 12:22:22 -04:00
|
|
|
debug!("PF beginning frame");
|
2019-03-09 12:57:44 -05:00
|
|
|
let mut params = unsafe { mem::zeroed() };
|
|
|
|
unsafe {
|
|
|
|
gl::BindFramebuffer(gl::FRAMEBUFFER, self.framebuffer_id);
|
|
|
|
MLGraphicsInitFrameParams(&mut params).unwrap();
|
|
|
|
let mut result = MLGraphicsBeginFrame(self.graphics_client, ¶ms, &mut self.frame_handle, &mut self.virtual_camera_array);
|
|
|
|
if result == ML_RESULT_TIMEOUT {
|
|
|
|
info!("PF frame timeout");
|
|
|
|
let mut sleep = Duration::from_millis(1);
|
|
|
|
let max_sleep = Duration::from_secs(5);
|
|
|
|
while result == ML_RESULT_TIMEOUT {
|
|
|
|
sleep = (sleep * 2).min(max_sleep);
|
|
|
|
info!("PF exponential backoff {}ms", sleep.as_millis());
|
|
|
|
thread::sleep(sleep);
|
|
|
|
result = MLGraphicsBeginFrame(self.graphics_client, ¶ms, &mut self.frame_handle, &mut self.virtual_camera_array);
|
|
|
|
}
|
|
|
|
info!("PF frame finished timeout");
|
|
|
|
}
|
|
|
|
result.unwrap();
|
|
|
|
}
|
|
|
|
let virtual_camera_array = &self.virtual_camera_array;
|
2019-03-28 12:22:22 -04:00
|
|
|
let initial_camera = self.initial_camera_transform.get_or_insert_with(|| {
|
|
|
|
let initial_offset = Transform3DF32::from_translation(0.0, 0.0, 1.0);
|
|
|
|
let mut camera = virtual_camera_array.virtual_cameras[0].transform;
|
|
|
|
for i in 1..virtual_camera_array.num_virtual_cameras {
|
|
|
|
let next = virtual_camera_array.virtual_cameras[i as usize].transform;
|
|
|
|
camera = camera.lerp(next, 1.0 / (i as f32 + 1.0));
|
|
|
|
}
|
|
|
|
Transform3DF32::from(camera).post_mul(&initial_offset)
|
|
|
|
});
|
2019-03-09 12:57:44 -05:00
|
|
|
let camera_transforms = (0..virtual_camera_array.num_virtual_cameras)
|
2019-03-28 12:22:22 -04:00
|
|
|
.map(|i| {
|
|
|
|
let camera = &virtual_camera_array.virtual_cameras[i as usize];
|
2019-03-27 17:40:09 -04:00
|
|
|
let projection = Transform3DF32::from(camera.projection);
|
|
|
|
let size = RectI32::from(virtual_camera_array.viewport).size();
|
|
|
|
let perspective = Perspective::new(&projection, size);
|
2019-03-28 12:22:22 -04:00
|
|
|
let view = Transform3DF32::from(camera.transform).inverse().post_mul(initial_camera);
|
2019-03-27 17:40:09 -04:00
|
|
|
CameraTransform { perspective, view }
|
|
|
|
})
|
|
|
|
.collect();
|
2019-03-09 12:57:44 -05:00
|
|
|
self.in_frame = true;
|
|
|
|
self.pose_event = Some(camera_transforms);
|
|
|
|
debug!("PF begun frame");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn end_frame(&mut self) {
|
|
|
|
if self.in_frame {
|
|
|
|
debug!("PF ending frame");
|
|
|
|
unsafe {
|
|
|
|
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
|
|
|
|
for i in 0..self.virtual_camera_array.num_virtual_cameras {
|
|
|
|
let virtual_camera = &self.virtual_camera_array.virtual_cameras[i as usize];
|
|
|
|
MLGraphicsSignalSyncObjectGL(self.graphics_client, virtual_camera.sync_object).unwrap();
|
|
|
|
}
|
|
|
|
MLGraphicsEndFrame(self.graphics_client, self.frame_handle).unwrap();
|
|
|
|
}
|
|
|
|
self.in_frame = false;
|
|
|
|
debug!("PF ended frame");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for MagicLeapWindow {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
self.end_frame();
|
|
|
|
unsafe {
|
|
|
|
gl::DeleteFramebuffers(1, &self.framebuffer_id);
|
|
|
|
MLGraphicsDestroyClient(&mut self.graphics_client);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Logging
|
|
|
|
|
|
|
|
pub struct MagicLeapLogger {
|
|
|
|
tag: CString,
|
|
|
|
level_filter: log::LevelFilter,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl log::Log for MagicLeapLogger {
|
|
|
|
fn enabled(&self, metadata: &log::Metadata) -> bool {
|
|
|
|
metadata.level() <= self.level_filter
|
|
|
|
}
|
|
|
|
|
|
|
|
fn log(&self, record: &log::Record) {
|
|
|
|
let lvl = match record.level() {
|
|
|
|
log::Level::Error => MLLogLevel::Error,
|
|
|
|
log::Level::Warn => MLLogLevel::Warning,
|
|
|
|
log::Level::Info => MLLogLevel::Info,
|
|
|
|
log::Level::Debug => MLLogLevel::Debug,
|
|
|
|
log::Level::Trace => MLLogLevel::Verbose,
|
|
|
|
};
|
|
|
|
let mut msg = SmallVec::<[u8; 128]>::new();
|
|
|
|
write!(msg, "{}\0", record.args()).unwrap();
|
|
|
|
unsafe {
|
|
|
|
MLLoggingLog(lvl, self.tag.as_ptr(), &msg[0] as *const _ as _);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn flush(&self) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MagicLeapLogger {
|
|
|
|
pub fn new(tag: CString, level_filter: log::LevelFilter) -> Self {
|
|
|
|
MagicLeapLogger { tag, level_filter }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-27 17:40:09 -04:00
|
|
|
// Linear interpolation
|
|
|
|
|
|
|
|
impl MLVec3f {
|
|
|
|
fn lerp(&self, other: MLVec3f, t: f32) -> MLVec3f {
|
|
|
|
MLVec3f {
|
|
|
|
x: util::lerp(self.x, other.x, t),
|
|
|
|
y: util::lerp(self.y, other.y, t),
|
|
|
|
z: util::lerp(self.z, other.z, t),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MLQuaternionf {
|
|
|
|
fn lerp(&self, other: MLQuaternionf, t: f32) -> MLQuaternionf {
|
|
|
|
MLQuaternionf {
|
|
|
|
x: util::lerp(self.x, other.x, t),
|
|
|
|
y: util::lerp(self.y, other.y, t),
|
|
|
|
z: util::lerp(self.z, other.z, t),
|
|
|
|
w: util::lerp(self.w, other.w, t),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MLTransform {
|
|
|
|
fn lerp(&self, other: MLTransform, t: f32) -> MLTransform {
|
|
|
|
MLTransform {
|
|
|
|
rotation: self.rotation.lerp(other.rotation, t),
|
|
|
|
position: self.position.lerp(other.position, t),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-09 12:57:44 -05:00
|
|
|
// Impl pathfinder traits for c-api types
|
|
|
|
|
|
|
|
impl From<MLTransform> for Transform3DF32 {
|
|
|
|
fn from(mat: MLTransform) -> Self {
|
|
|
|
Transform3DF32::from(mat.rotation)
|
|
|
|
.pre_mul(&Transform3DF32::from(mat.position))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<MLVec3f> for Transform3DF32 {
|
|
|
|
fn from(v: MLVec3f) -> Self {
|
|
|
|
Transform3DF32::from_translation(v.x, v.y, v.z)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<MLRectf> for RectF32 {
|
|
|
|
fn from(r: MLRectf) -> Self {
|
|
|
|
RectF32::new(Point2DF32::new(r.x, r.y), Point2DF32::new(r.w, r.h))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<MLRectf> for RectI32 {
|
|
|
|
fn from(r: MLRectf) -> Self {
|
|
|
|
RectF32::from(r).to_i32()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<MLQuaternionf> for Transform3DF32 {
|
|
|
|
fn from(q: MLQuaternionf) -> Self {
|
|
|
|
Transform3DF32::from_rotation_quaternion(F32x4::new(q.x, q.y, q.z, q.w))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<MLMat4f> for Transform3DF32 {
|
|
|
|
fn from(mat: MLMat4f) -> Self {
|
|
|
|
let a = mat.matrix_colmajor;
|
|
|
|
Transform3DF32::row_major(a[0], a[4], a[8], a[12],
|
|
|
|
a[1], a[5], a[9], a[13],
|
|
|
|
a[2], a[6], a[10], a[14],
|
|
|
|
a[3], a[7], a[11], a[15])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|