diff --git a/Cargo.toml b/Cargo.toml index 2f3a55f3..c6c528c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "demo/android/rust", "demo/common", + "demo/magicleap", "demo/native", "geometry", "gl", diff --git a/demo/magicleap/.cargo/config b/demo/magicleap/.cargo/config new file mode 100644 index 00000000..33795c4a --- /dev/null +++ b/demo/magicleap/.cargo/config @@ -0,0 +1,3 @@ +[target.aarch64-linux-android] +linker = "./fake-ld.sh" +ar = "aarch64-linux-android-ar" diff --git a/demo/magicleap/.gitignore b/demo/magicleap/.gitignore new file mode 100644 index 00000000..4efeb1bf --- /dev/null +++ b/demo/magicleap/.gitignore @@ -0,0 +1 @@ +.out/ diff --git a/demo/magicleap/Cargo.toml b/demo/magicleap/Cargo.toml new file mode 100644 index 00000000..75d5c6e1 --- /dev/null +++ b/demo/magicleap/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "pathfinder_immersive_demo" +version = "0.1.0" +edition = "2018" +authors = ["Alan Jeffrey "] + +[dependencies] +gl = "0.6" +rayon = "1.0" +usvg = "0.4" +egl = "0.2" +log = "0.4" +smallvec = "0.6" +glutin = { version = "0.19", optional = true } + +[lib] +crate-type = ["staticlib"] + +[features] +mocked = ["glutin"] + +[dependencies.pathfinder_demo] +path = "../common" + +[dependencies.pathfinder_geometry] +path = "../../geometry" + +[dependencies.pathfinder_gl] +path = "../../gl" + +[dependencies.pathfinder_gpu] +path = "../../gpu" + +[dependencies.pathfinder_renderer] +path = "../../renderer" + +[dependencies.pathfinder_simd] +path = "../../simd" + +[dependencies.pathfinder_svg] +path = "../../svg" + +[dependencies.pathfinder_ui] +path = "../../ui" diff --git a/demo/magicleap/PathfinderDemo.mabu b/demo/magicleap/PathfinderDemo.mabu new file mode 100644 index 00000000..17ec0e3e --- /dev/null +++ b/demo/magicleap/PathfinderDemo.mabu @@ -0,0 +1,19 @@ +KIND = program +SRCS = src/main.cpp + +LIBPATHS.debug = \ + ../../target/aarch64-linux-android/debug + +LIBPATHS.release = \ + ../../target/aarch64-linux-android/release + +USES = ml_sdk OpenGL stdc++ + +STLIBS = \ + pathfinder_immersive_demo + +SHLIBS = \ + ml_privileges + +DATAS = \ + ../../resources/** : resources/ diff --git a/demo/magicleap/PathfinderDemo.package b/demo/magicleap/PathfinderDemo.package new file mode 100644 index 00000000..de059c08 --- /dev/null +++ b/demo/magicleap/PathfinderDemo.package @@ -0,0 +1,2 @@ +REFS = PathfinderDemo +OPTIONS=package/debuggable/on diff --git a/demo/magicleap/fake-ld.sh b/demo/magicleap/fake-ld.sh new file mode 100755 index 00000000..88f32377 --- /dev/null +++ b/demo/magicleap/fake-ld.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# This shell script strips out the -landroid that is passed by default by rustc to +# the linker on aarch64-linux-android, and adds some entries to the ld search path. + +set -o errexit +set -o nounset +set -o pipefail + +TARGET=${TARGET:-"aarch64-linux-android"} +LD=${LD:-"${MAGICLEAP_SDK}/tools/toolchains/bin/${TARGET}-gcc"} +LDFLAGS=${LDFLAGS:-"--sysroot=${MAGICLEAP_SDK}/lumin -L${MAGICLEAP_SDK}/tools/toolchains/lib/gcc/${TARGET}/4.9.x ${MAGICLEAP_SDK}/lumin/usr/lib/crtbegin_so.o"} + +# Remove the -landroid flag, grr +ARGS=("$@") +ARGS=${ARGS[@]/-landroid} + +${LD} ${LDFLAGS} ${ARGS} diff --git a/demo/magicleap/manifest.xml b/demo/magicleap/manifest.xml new file mode 100644 index 00000000..3377bed5 --- /dev/null +++ b/demo/magicleap/manifest.xml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/demo/magicleap/src/c_api.rs b/demo/magicleap/src/c_api.rs new file mode 100644 index 00000000..6f0992f6 --- /dev/null +++ b/demo/magicleap/src/c_api.rs @@ -0,0 +1,281 @@ +#![allow(dead_code)] + +use gl::types::GLuint; +use std::error::Error; +use std::ffi::CStr; +use std::fmt; +#[cfg(not(feature = "mocked"))] +use std::os::raw::c_char; +use std::os::raw::c_void; + +// Types from the MagicLeap C API + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(C)] +pub struct MLHandle(u64); + +impl MLHandle { + pub fn as_gl_uint(self) -> GLuint { + self.0 as GLuint + } +} + +impl From<*mut T> for MLHandle { + fn from(ptr: *mut T) -> MLHandle { + MLHandle(ptr as u64) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(C)] +pub struct MLResult(u32); + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct MLGraphicsOptions { + pub graphics_flags: u32, + pub color_format: MLSurfaceFormat, + pub depth_format: MLSurfaceFormat, +} + +impl Default for MLGraphicsOptions { + fn default() -> MLGraphicsOptions { + MLGraphicsOptions { + graphics_flags: 0, + color_format: MLSurfaceFormat::RGBA8UNormSRGB, + depth_format: MLSurfaceFormat::D32Float, + } + } +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct MLGraphicsRenderTargetsInfo { + pub min_clip: f32, + pub max_clip: f32, + pub num_virtual_cameras: u32, + pub buffers: [MLGraphicsRenderBufferInfo; ML_BUFFER_COUNT], +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct MLGraphicsRenderBufferInfo { + pub color: MLGraphicsRenderTarget, + pub depth: MLGraphicsRenderTarget, +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct MLGraphicsRenderTarget { + pub width: u32, + pub height: u32, + pub id: MLHandle, + pub format: MLSurfaceFormat, +} + +#[derive(Clone, Copy, Debug)] +#[repr(u32)] +pub enum MLSurfaceFormat { + Unknown = 0, + RGBA8UNorm, + RGBA8UNormSRGB, + RGB10A2UNorm, + RGBA16Float, + D32Float, + D24NormS8, + D32FloatS8, +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct MLGraphicsVirtualCameraInfoArray { + pub num_virtual_cameras: u32, + pub color_id: MLHandle, + pub depth_id: MLHandle, + pub viewport: MLRectf, + pub virtual_cameras: [MLGraphicsVirtualCameraInfo; ML_VIRTUAL_CAMERA_COUNT], +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct MLGraphicsVirtualCameraInfo { + pub left_half_angle: f32, + pub right_half_angle: f32, + pub top_half_angle: f32, + pub bottom_half_angle: f32, + pub sync_object: MLHandle, + pub projection: MLMat4f, + pub transform: MLTransform, + pub virtual_camera_name: MLGraphicsVirtualCameraName, +} + +#[derive(Clone, Copy, Debug)] +#[repr(i32)] +pub enum MLGraphicsVirtualCameraName { + Combined = -1, + Left = 0, + Right, +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct MLGraphicsFrameParams { + pub near_clip: f32, + pub far_clip: f32, + pub focus_distance: f32, + pub surface_scale: f32, + pub protected_surface: bool, + pub projection_type: MLGraphicsProjectionType, +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct MLSnapshotPtr(*mut c_void); + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct MLCoordinateFrameUID { + pub data: [u64; 2], +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct MLHeadTrackingStaticData { + pub coord_frame_head: MLCoordinateFrameUID, +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct MLGraphicsClipExtentsInfo { + pub virtual_camera_name: MLGraphicsVirtualCameraName, + pub projection: MLMat4f, + pub transform: MLTransform, +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct MLGraphicsClipExtentsInfoArray { + pub num_virtual_cameras: u32, + pub full_extents: MLGraphicsClipExtentsInfo, + pub virtual_camera_extents: [MLGraphicsClipExtentsInfo; ML_VIRTUAL_CAMERA_COUNT], +} + +#[derive(Clone, Copy, Debug)] +#[repr(u32)] +pub enum MLGraphicsProjectionType { + SignedZ = 0, + ReversedInfiniteZ = 1, + UnsignedZ = 2, +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct MLTransform { + pub rotation: MLQuaternionf, + pub position: MLVec3f, +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct MLVec3f { + pub x: f32, + pub y: f32, + pub z: f32, +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct MLRectf { + pub x: f32, + pub y: f32, + pub w: f32, + pub h: f32, +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct MLQuaternionf { + pub x: f32, + pub y: f32, + pub z: f32, + pub w: f32, +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct MLMat4f { + pub matrix_colmajor: [f32; 16], +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum MLLogLevel { + Fatal = 0, + Error = 1, + Warning = 2, + Info = 3, + Debug = 4, + Verbose = 5, +} + +// Constants from the MagicLeap C API + +pub const ML_RESULT_OK: MLResult = MLResult(0); +pub const ML_RESULT_TIMEOUT: MLResult = MLResult(2); +pub const ML_RESULT_UNSPECIFIED_FAILURE: MLResult = MLResult(4); +pub const ML_HANDLE_INVALID: MLHandle = MLHandle(0xFFFFFFFFFFFFFFFF); +pub const ML_BUFFER_COUNT: usize = 3; +pub const ML_VIRTUAL_CAMERA_COUNT: usize = 2; + +// ML error handling + +impl fmt::Display for MLResult { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + let cmessage = unsafe { CStr::from_ptr(MLGetResultString(*self)) }; + let message = cmessage.to_str().or(Err(fmt::Error))?; + formatter.write_str(message) + } +} + +impl MLResult { + pub fn ok(self) -> Result<(), MLResult> { + if self == ML_RESULT_OK { + Ok(()) + } else { + Err(self) + } + } + + pub fn unwrap(self) { + self.ok().unwrap() + } +} + +impl Error for MLResult { +} + +// Functions from the MagicLeap C API + +#[cfg(not(feature = "mocked"))] +extern "C" { + pub fn MLGraphicsCreateClientGL(options: *const MLGraphicsOptions, gl_context: MLHandle, graphics_client : &mut MLHandle) -> MLResult; + pub fn MLGraphicsDestroyClient(graphics_client: *mut MLHandle) -> MLResult; + pub fn MLHeadTrackingCreate(tracker: *mut MLHandle) -> MLResult; + pub fn MLHeadTrackingGetStaticData(head_tracker: MLHandle, data: *mut MLHeadTrackingStaticData) -> MLResult; + pub fn MLPerceptionGetSnapshot(snapshot: *mut MLSnapshotPtr) -> MLResult; + pub fn MLSnapshotGetTransform(snapshot: MLSnapshotPtr, id: *const MLCoordinateFrameUID, transform: *mut MLTransform) -> MLResult; + pub fn MLPerceptionReleaseSnapshot(snapshot: MLSnapshotPtr) -> MLResult; + pub fn MLLifecycleSetReadyIndication() -> MLResult; + pub fn MLGraphicsGetClipExtents(graphics_client: MLHandle, array: *mut MLGraphicsClipExtentsInfoArray) -> MLResult; + pub fn MLGraphicsGetRenderTargets(graphics_client: MLHandle, targets: *mut MLGraphicsRenderTargetsInfo) -> MLResult; + pub fn MLGraphicsInitFrameParams(params: *mut MLGraphicsFrameParams) -> MLResult; + pub fn MLGraphicsBeginFrame(graphics_client: MLHandle, params: *const MLGraphicsFrameParams, frame_handle: *mut MLHandle, virtual_camera_array: *mut MLGraphicsVirtualCameraInfoArray) -> MLResult; + pub fn MLGraphicsEndFrame(graphics_client: MLHandle, frame_handle: MLHandle) -> MLResult; + pub fn MLGraphicsSignalSyncObjectGL(graphics_client: MLHandle, sync_object: MLHandle) -> MLResult; + pub fn MLGetResultString(result_code: MLResult) -> *const c_char; + pub fn MLLoggingLogLevelIsEnabled(lvl: MLLogLevel) -> bool; + pub fn MLLoggingLog(lvl: MLLogLevel, tag: *const c_char, message: *const c_char); +} + +#[cfg(feature = "mocked")] +pub use crate::mocked_c_api::*; diff --git a/demo/magicleap/src/display.rs b/demo/magicleap/src/display.rs new file mode 100644 index 00000000..6a0f94ba --- /dev/null +++ b/demo/magicleap/src/display.rs @@ -0,0 +1,46 @@ +// pathfinder/demo/immersive/display.rs +// +// Copyright © 2019 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::error::Error; +use std::io; +use pathfinder_geometry::basic::point::Point2DI32; +use pathfinder_geometry::basic::rect::RectI32; +use pathfinder_geometry::basic::transform3d::Perspective; +use pathfinder_geometry::basic::transform3d::Transform3DF32; +use pathfinder_gl::GLVersion; +use pathfinder_gpu::resources::ResourceLoader; + +pub trait Display: Sized { + type Error: DisplayError; + type Camera: DisplayCamera; + + fn resource_loader(&self) -> &dyn ResourceLoader; + fn gl_version(&self) -> GLVersion; + fn make_current(&mut self) -> Result<(), Self::Error>; + + fn running(&self) -> bool; + fn size(&self) -> Point2DI32; + + fn begin_frame(&mut self) -> Result<&mut[Self::Camera], Self::Error>; + fn end_frame(&mut self) -> Result<(), Self::Error>; +} + +pub trait DisplayCamera { + type Error: DisplayError; + + fn bounds(&self) -> RectI32; + fn view(&self) -> Transform3DF32; + fn perspective(&self) -> Perspective; + + fn make_current(&mut self) -> Result<(), Self::Error>; +} + +pub trait DisplayError: Error + From + From{ +} diff --git a/demo/magicleap/src/glwindow.rs b/demo/magicleap/src/glwindow.rs new file mode 100644 index 00000000..bd90d08e --- /dev/null +++ b/demo/magicleap/src/glwindow.rs @@ -0,0 +1,254 @@ +// pathfinder/demo/immersive/glwindow.rs +// +// Copyright © 2019 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use gl; + +use glutin::ContextBuilder; +use glutin::ContextError; +use glutin::CreationError; +use glutin::EventsLoop; +use glutin::Event; +use glutin::WindowEvent; +use glutin::GlContext; +use glutin::GlWindow; +use glutin::WindowBuilder; +use glutin::dpi::LogicalSize; + +use crate::display::Display; +use crate::display::DisplayCamera; +use crate::display::DisplayError; + +use pathfinder_geometry::basic::point::Point2DI32; +use pathfinder_geometry::basic::rect::RectI32; +use pathfinder_geometry::basic::transform3d::Transform3DF32; +use pathfinder_geometry::basic::transform3d::Perspective; +use pathfinder_gl::GLVersion; +use pathfinder_gpu::resources::FilesystemResourceLoader; +use pathfinder_gpu::resources::ResourceLoader; + +use std::env; +use std::error::Error; +use std::fmt; +use std::f32::consts::FRAC_PI_4; +use std::io; +use std::rc::Rc; +use std::time::Instant; + +use usvg; + +pub struct GlWindowDisplay { + events_loop: EventsLoop, + gl_window: Rc, + running: bool, + cameras: Vec, + resource_loader: FilesystemResourceLoader, +} + +pub struct GlWindowCamera { + eye: Eye, + gl_window: Rc, + start: Instant, +} + +enum Eye { + Left, + Right, +} + +#[derive(Debug)] +pub enum GlWindowError { + Creation(CreationError), + Context(ContextError), + SVG(usvg::Error), + IO(io::Error), +} + +const DEFAULT_EYE_WIDTH: u32 = 1024; +const DEFAULT_EYE_HEIGHT: u32 = 768; + +const CAMERA_DISTANCE: f32 = 3.0; +const NEAR_CLIP_PLANE: f32 = 0.01; +const FAR_CLIP_PLANE: f32 = 10.0; + +impl Display for GlWindowDisplay { + type Error = GlWindowError; + type Camera = GlWindowCamera; + + fn resource_loader(&self) -> &dyn ResourceLoader { + &self.resource_loader + } + + fn gl_version(&self) -> GLVersion { + GLVersion::GL3 + } + + fn make_current(&mut self) -> Result<(), GlWindowError> { + let size = self.size(); + unsafe { + self.gl_window.make_current()?; + gl::Viewport(0, 0, size.x(), size.y()); + gl::Scissor(0, 0, size.x(), size.y()); + gl::Enable(gl::SCISSOR_TEST); + } + self.handle_events(); + Ok(()) + } + + fn begin_frame(&mut self) -> Result<&mut[GlWindowCamera], GlWindowError> { + self.handle_events(); + Ok(&mut self.cameras[..]) + } + + fn end_frame(&mut self) -> Result<(), GlWindowError> { + self.handle_events(); + self.gl_window.swap_buffers()?; + self.handle_events(); + Ok(()) + } + + fn running(&self) -> bool { + self.running + } + + fn size(&self) -> Point2DI32 { + window_size(&*self.gl_window) + } +} + +impl DisplayCamera for GlWindowCamera { + type Error = GlWindowError; + + fn make_current(&mut self) -> Result<(), GlWindowError> { + let bounds = self.bounds(); + unsafe { + self.gl_window.make_current()?; + gl::Viewport(bounds.origin().x(), bounds.origin().y(), bounds.size().x(), bounds.size().y()); + gl::Scissor(bounds.origin().x(), bounds.origin().y(), bounds.size().x(), bounds.size().y()); + } + Ok(()) + } + + fn bounds(&self) -> RectI32 { + let window_size = window_size(&*self.gl_window); + let eye_size = Point2DI32::new(window_size.x()/2, window_size.y()); + let origin = match self.eye { + Eye::Left => Point2DI32::new(0, 0), + Eye::Right => Point2DI32::new(eye_size.x(), 0), + }; + RectI32::new(origin, eye_size) + } + + fn perspective(&self) -> Perspective { + // TODO: add eye offsets + let bounds = self.bounds(); + let aspect = bounds.size().x() as f32 / bounds.size().y() as f32; + let transform = Transform3DF32::from_perspective(FRAC_PI_4, aspect, NEAR_CLIP_PLANE, FAR_CLIP_PLANE); + Perspective::new(&transform, bounds.size()) + } + + fn view(&self) -> Transform3DF32 { + let duration = Instant::now() - self.start; + let rotation = duration.as_millis() as f32 / 1000.0; + Transform3DF32::from_rotation(rotation, 0.0, 0.0) + .pre_mul(&Transform3DF32::from_translation(0.0, 0.0, -CAMERA_DISTANCE)) + } +} + +impl GlWindowDisplay { + pub fn new() -> Result { + let resource_loader = FilesystemResourceLoader::locate(); + let size = default_window_size(); + let events_loop = glutin::EventsLoop::new(); + let window = WindowBuilder::new() + .with_title("Pathfinder Immersive Demo") + .with_dimensions(size); + let context = ContextBuilder::new() + .with_vsync(true); + let gl_window = Rc::new(glutin::GlWindow::new(window, context, &events_loop)?); + let start = Instant::now(); + let cameras = vec![ + GlWindowCamera { gl_window: gl_window.clone(), start, eye: Eye::Left }, + GlWindowCamera { gl_window: gl_window.clone(), start, eye: Eye::Right }, + ]; + gl::load_with(|name| gl_window.get_proc_address(name) as *const _); + Ok(GlWindowDisplay { + resource_loader, + events_loop, + gl_window, + cameras, + running: true, + }) + } + + fn handle_events(&mut self) { + let running = &mut self.running; + self.events_loop.poll_events(|event| { + match event { + Event::WindowEvent { event: WindowEvent::CloseRequested, .. } | + Event::WindowEvent { event: WindowEvent::Destroyed, .. } => *running = false, + _ => (), + } + }) + } +} + +fn window_size(gl_window: &GlWindow) -> Point2DI32 { + let logical = gl_window + .get_inner_size() + .unwrap_or_else(|| default_window_size()); + let hidpi = gl_window.get_hidpi_factor(); + let physical = logical.to_physical(hidpi); + Point2DI32::new(physical.width as i32, physical.height as i32) +} + +fn default_window_size() -> LogicalSize { + LogicalSize::new((DEFAULT_EYE_WIDTH * 2) as f64, DEFAULT_EYE_HEIGHT as f64) +} + +impl fmt::Display for GlWindowError { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + GlWindowError::Creation(ref err) => err.fmt(formatter), + GlWindowError::Context(ref err) => err.fmt(formatter), + GlWindowError::SVG(ref err) => err.fmt(formatter), + GlWindowError::IO(ref err) => err.fmt(formatter), + } + } +} + +impl Error for GlWindowError { +} + +impl From for GlWindowError { + fn from(err: CreationError) -> GlWindowError { + GlWindowError::Creation(err) + } +} + +impl From for GlWindowError { + fn from(err: ContextError) -> GlWindowError { + GlWindowError::Context(err) + } +} + +impl From for GlWindowError { + fn from(err: usvg::Error) -> GlWindowError { + GlWindowError::SVG(err) + } +} + +impl From for GlWindowError { + fn from(err: io::Error) -> GlWindowError { + GlWindowError::IO(err) + } +} + +impl DisplayError for GlWindowError { +} \ No newline at end of file diff --git a/demo/magicleap/src/immersive.rs b/demo/magicleap/src/immersive.rs new file mode 100644 index 00000000..775a0b37 --- /dev/null +++ b/demo/magicleap/src/immersive.rs @@ -0,0 +1,129 @@ +#![allow(unused_imports)] +#![allow(dead_code)] + +use crate::display::Display; +use crate::display::DisplayCamera; + +use log::debug; + +use pathfinder_demo::Options; +use pathfinder_demo::BuildOptions; +use pathfinder_demo::SceneThreadProxy; +use pathfinder_demo::MainToSceneMsg; +use pathfinder_demo::SceneToMainMsg; +use pathfinder_demo::Camera; +use pathfinder_demo::CameraTransform3D; +use pathfinder_gl::GLDevice; +use pathfinder_renderer::gpu::renderer::Renderer; +use pathfinder_geometry::basic::point::Point2DI32; +use pathfinder_geometry::basic::point::Point2DF32; +use pathfinder_geometry::basic::point::Point3DF32; +use pathfinder_geometry::basic::rect::RectI32; +use pathfinder_geometry::basic::transform2d::Transform2DF32; +use pathfinder_geometry::basic::transform3d::Transform3DF32; +use pathfinder_geometry::basic::transform3d::Perspective; +use pathfinder_gpu::Device; +use pathfinder_simd::default::F32x4; +use pathfinder_svg::BuiltSVG; +use pathfinder_renderer::scene::Scene; +use pathfinder_renderer::builder::RenderTransform; + +use std::error::Error; +use std::fmt; +use std::path::Path; +use std::path::PathBuf; +use std::time::Instant; + +use usvg; + +pub struct ImmersiveDemo { + display: D, + renderer: Renderer, + scene_thread_proxy: SceneThreadProxy, + svg_size: Point2DF32, + svg_to_world: Option, +} + +static DEFAULT_SVG_VIRTUAL_PATH: &'static str = "svg/Ghostscript_Tiger.svg"; + +// SVG dimensions in metres +const MAX_SVG_HEIGHT: f32 = 1.0; +const MAX_SVG_WIDTH: f32 = 1.0; +const DEFAULT_SVG_DISTANCE: f32 = 1.5; + +impl ImmersiveDemo { + pub fn new(mut display: D) -> Result { + display.make_current()?; + let resources = display.resource_loader(); + let options = Options::get(); + let svg_data = resources.slurp(DEFAULT_SVG_VIRTUAL_PATH)?; + let tree = usvg::Tree::from_data(&svg_data[..], &usvg::Options::default())?; + let svg = BuiltSVG::from_tree(tree); + let svg_size = svg.scene.view_box.size(); + let scene_thread_proxy = SceneThreadProxy::new(svg.scene, options); + let _ = scene_thread_proxy.sender.send(MainToSceneMsg::SetDrawableSize(display.size())); + let device = GLDevice::new(display.gl_version()); + let viewport = RectI32::new(Point2DI32::new(0, 0), display.size()); + let renderer = Renderer::new(device, resources, viewport, display.size()); + Ok(ImmersiveDemo { + display, + renderer, + scene_thread_proxy, + svg_size, + svg_to_world: None, + }) + } + + pub fn running(&self) -> bool { + self.display.running() + } + + pub fn render_scene(&mut self) -> Result<(), D::Error> { + self.display.make_current()?; + let cameras = self.display.begin_frame()?; + + debug!("PF rendering a frame"); + let start = Instant::now(); + + let svg_size = self.svg_size; + let svg_to_world = self.svg_to_world.get_or_insert_with(|| { + let view: Transform3DF32 = cameras[0].view(); + let svg_to_world_scale = f32::max(MAX_SVG_WIDTH / svg_size.x(), MAX_SVG_HEIGHT / svg_size.y()); + let svg_width = svg_size.x() * svg_to_world_scale; + let svg_height = svg_size.y() * svg_to_world_scale; + Transform3DF32::from_uniform_scale(svg_to_world_scale) + .pre_mul(&Transform3DF32::from_translation(-svg_width / 2.0, -svg_height / 2.0, -DEFAULT_SVG_DISTANCE)) + .pre_mul(&Transform3DF32::from_scale(1.0, -1.0, 1.0)) + .pre_mul(&view.inverse()) + }); + + let render_transforms = cameras.iter() + .map(|camera| RenderTransform::Perspective( + camera.perspective() + .post_mul(&camera.view()) + .post_mul(&svg_to_world) + )).collect(); + let msg = MainToSceneMsg::Build(BuildOptions { + render_transforms: render_transforms, + stem_darkening_font_size: None, + }); + let _ = self.scene_thread_proxy.sender.send(msg); + + if let Ok(reply) = self.scene_thread_proxy.receiver.recv() { + for (camera, scene) in cameras.iter_mut().zip(reply.render_scenes) { + debug!("PF rendering eye after {}ms", (Instant::now() - start).as_millis()); + camera.make_current()?; + let bounds = camera.bounds(); + let background = F32x4::new(0.0, 0.0, 0.0, 1.0); + self.renderer.device.clear(Some(background), Some(1.0), Some(0)); + self.renderer.enable_depth(); + self.renderer.set_viewport(bounds); + self.renderer.render_scene(&scene.built_scene); + } + } + + debug!("PF rendered frame after {}ms", (Instant::now() - start).as_millis()); + self.display.end_frame()?; + Ok(()) + } +} diff --git a/demo/magicleap/src/lib.rs b/demo/magicleap/src/lib.rs new file mode 100644 index 00000000..ebe1e57a --- /dev/null +++ b/demo/magicleap/src/lib.rs @@ -0,0 +1,57 @@ +use crate::magicleap::MagicLeapLogger; +use crate::magicleap::MagicLeapWindow; + +use egl; +use egl::EGLContext; +use egl::EGLDisplay; + +use log::debug; + +use pathfinder_demo::Background; +use pathfinder_demo::DemoApp; +use pathfinder_demo::Options; +use pathfinder_demo::window::Mode; + +use std::ffi::CString; + +mod c_api; +mod magicleap; + +#[cfg(feature = "mocked")] +mod mocked_c_api; + +#[no_mangle] +pub extern "C" fn magicleap_pathfinder_demo(egl_display: EGLDisplay, egl_context: EGLContext) { + unsafe { c_api::MLLoggingLog(c_api::MLLogLevel::Info, &b"Pathfinder Demo\0"[0], &b"Initializing\0"[0]) }; + + let tag = CString::new("Pathfinder Demo").unwrap(); + let level = log::LevelFilter::Warn; + let logger = MagicLeapLogger::new(tag, level); + log::set_boxed_logger(Box::new(logger)).unwrap(); + log::set_max_level(level); + debug!("Initialized logging"); + + let window = MagicLeapWindow::new(egl_display, egl_context); + let window_size = window.size(); + + let mut options = Options::default(); + options.ui = false; + options.background = Background::None; + options.mode = Mode::VR; + + let mut app = DemoApp::new(window, window_size, options); + debug!("Initialized app"); + + while app.window.running() { + let mut events = Vec::new(); + while let Some(event) = app.window.try_get_event() { + events.push(event); + } + + let scene_count = app.prepare_frame(events); + for scene_index in 0..scene_count { + app.draw_scene(scene_index); + } + app.finish_drawing_frame(); + } +} \ No newline at end of file diff --git a/demo/magicleap/src/magicleap.rs b/demo/magicleap/src/magicleap.rs new file mode 100644 index 00000000..a1275b0e --- /dev/null +++ b/demo/magicleap/src/magicleap.rs @@ -0,0 +1,362 @@ +// pathfinder/demo/magicleap/magicleap.rs +// +// Copyright © 2019 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , 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 log::warn; + +use pathfinder_demo::window::CameraTransform; +use pathfinder_demo::window::Event; +use pathfinder_demo::window::Mode; +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; +use pathfinder_gl::GLVersion; +use pathfinder_gpu::resources::FilesystemResourceLoader; +use pathfinder_gpu::resources::ResourceLoader; +use pathfinder_simd::default::F32x4; + +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, + initial_camera_transforms: Option>, + frame_handle: MLHandle, + resource_loader: FilesystemResourceLoader, + pose_event: Option>, + 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 + } + + 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 { + Err(()) + } + + fn view_box_size(&self, _mode: Mode) -> Point2DI32 { + self.size + } + + fn barrel_distortion_coefficients(&self) -> BarrelDistortionCoefficients { + BarrelDistortionCoefficients { k0: 0.0, k1: 0.0 } + } + + fn make_current(&mut self, _mode: Mode, eye: Option) -> RectI32 { + self.begin_frame(); + let eye = match eye { + Some(eye) if (eye as usize) < ML_VIRTUAL_CAMERA_COUNT => eye as usize, + _ => { warn!("Asked for eye out of range {:?}", eye); 0 } + }; + 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); + RectI32::new(Point2DI32::new(0, 0), self.size) + } + + fn present(&mut self) { + self.end_frame(); + self.begin_frame(); + } +} + +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, + initial_camera_transforms: None, + 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 { + self.pose_event.take().map(Event::CameraTransforms) + } + + fn begin_frame(&mut self) { + if !self.in_frame { + debug!("PF beginning frame"); + 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; + let initial_cameras = self.initial_camera_transforms.get_or_insert_with(|| + (0..virtual_camera_array.num_virtual_cameras) + .map(|i| &virtual_camera_array.virtual_cameras[i as usize]) + .map(|camera| Transform3DF32::from(camera.transform).post_mul(&Transform3DF32::from_uniform_scale(0.5))) + .collect() + ); + let camera_transforms = (0..virtual_camera_array.num_virtual_cameras) + .map(|i| &virtual_camera_array.virtual_cameras[i as usize]) + .zip(initial_cameras) + .map(|(camera, initial_camera)| CameraTransform { + perspective: Perspective::new( + &Transform3DF32::from(camera.projection), + RectI32::from(virtual_camera_array.viewport).size(), + ), + view: Transform3DF32::from(camera.transform).inverse().post_mul(initial_camera), + }).collect(); + 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 } + } +} + +// Impl pathfinder traits for c-api types + +impl From for Transform3DF32 { + fn from(mat: MLTransform) -> Self { + Transform3DF32::from(mat.rotation) + .pre_mul(&Transform3DF32::from(mat.position)) + } +} + +impl From for Transform3DF32 { + fn from(v: MLVec3f) -> Self { + Transform3DF32::from_translation(v.x, v.y, v.z) + } +} + +impl From for RectF32 { + fn from(r: MLRectf) -> Self { + RectF32::new(Point2DF32::new(r.x, r.y), Point2DF32::new(r.w, r.h)) + } +} + +impl From for RectI32 { + fn from(r: MLRectf) -> Self { + RectF32::from(r).to_i32() + } +} + +impl From for Transform3DF32 { + fn from(q: MLQuaternionf) -> Self { + Transform3DF32::from_rotation_quaternion(F32x4::new(q.x, q.y, q.z, q.w)) + } +} + +impl From 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]) + } +} + diff --git a/demo/magicleap/src/main.cpp b/demo/magicleap/src/main.cpp new file mode 100644 index 00000000..666e6bbc --- /dev/null +++ b/demo/magicleap/src/main.cpp @@ -0,0 +1,179 @@ +#include +#include + +#include +#include + +#ifndef EGL_EGLEXT_PROTOTYPES +#define EGL_EGLEXT_PROTOTYPES +#endif + +#include +#include + +#ifndef GL_GLEXT_PROTOTYPES +#define GL_GLEXT_PROTOTYPES +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include + +// Entry point to the Rust code +extern "C" MLResult magicleap_pathfinder_demo(EGLDisplay egl_display, EGLContext egl_context); + +// Constants +const char application_name[] = "com.mozilla.pathfinder.demo"; + +// Structures +struct application_context_t { + int dummy_value; +}; + +struct graphics_context_t { + + EGLDisplay egl_display; + EGLContext egl_context; + + GLuint framebuffer_id; + GLuint vertex_shader_id; + GLuint fragment_shader_id; + GLuint program_id; + + graphics_context_t(); + ~graphics_context_t(); + + void makeCurrent(); + void swapBuffers(); + void unmakeCurrent(); +}; + +graphics_context_t::graphics_context_t() { + egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + + EGLint major = 4; + EGLint minor = 0; + eglInitialize(egl_display, &major, &minor); + eglBindAPI(EGL_OPENGL_API); + + EGLint config_attribs[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 0, + EGL_DEPTH_SIZE, 24, + EGL_STENCIL_SIZE, 8, + EGL_NONE + }; + EGLConfig egl_config = nullptr; + EGLint config_size = 0; + eglChooseConfig(egl_display, config_attribs, &egl_config, 1, &config_size); + + EGLint context_attribs[] = { + EGL_CONTEXT_MAJOR_VERSION_KHR, 3, + EGL_CONTEXT_MINOR_VERSION_KHR, 0, + EGL_NONE + }; + egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, context_attribs); +} + +void graphics_context_t::makeCurrent() { + eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_context); +} + +void graphics_context_t::unmakeCurrent() { + eglMakeCurrent(NULL, EGL_NO_SURFACE, EGL_NO_SURFACE, NULL); +} + +void graphics_context_t::swapBuffers() { + // buffer swapping is implicit on device (MLGraphicsEndFrame) +} + +graphics_context_t::~graphics_context_t() { + eglDestroyContext(egl_display, egl_context); + eglTerminate(egl_display); +} + +// Callbacks +static void onStop(void* application_context) +{ + ((struct application_context_t*)application_context)->dummy_value = 0; + ML_LOG(Info, "%s: On stop called.", application_name); +} + +static void onPause(void* application_context) +{ + ((struct application_context_t*)application_context)->dummy_value = 1; + ML_LOG(Info, "%s: On pause called.", application_name); +} + +static void onResume(void* application_context) +{ + ((struct application_context_t*)application_context)->dummy_value = 2; + ML_LOG(Info, "%s: On resume called.", application_name); +} + +extern "C" void logMessage(MLLogLevel lvl, char* msg) { + if (MLLoggingLogLevelIsEnabled(lvl)) { + MLLoggingLog(lvl, ML_DEFAULT_LOG_TAG, msg); + } +} + +int main() { + // set up host-specific graphics surface + graphics_context_t graphics_context; + + // let system know our app has started + MLLifecycleCallbacks lifecycle_callbacks = {}; + lifecycle_callbacks.on_stop = onStop; + lifecycle_callbacks.on_pause = onPause; + lifecycle_callbacks.on_resume = onResume; + + struct application_context_t application_context; + application_context.dummy_value = 2; + + if (MLResult_Ok != MLLifecycleInit(&lifecycle_callbacks, (void*)&application_context)) { + ML_LOG(Error, "%s: Failed to initialize lifecycle.", application_name); + return -1; + } + + if (MLResult_Ok != MLPrivilegesStartup()) { + ML_LOG(Error, "%s: Failed to initialize privileges.", application_name); + return -1; + } + if (MLPrivilegesRequestPrivilege(MLPrivilegeID_WorldReconstruction) != MLPrivilegesResult_Granted) { + ML_LOG(Error, "Privilege %d denied.", MLPrivilegeID_WorldReconstruction); + return -1; + } + if (MLPrivilegesRequestPrivilege(MLPrivilegeID_LowLatencyLightwear) != MLPrivilegesResult_Granted) { + ML_LOG(Error, "Privilege %d denied.", MLPrivilegeID_LowLatencyLightwear); + return -1; + } + + // initialize perception system + MLPerceptionSettings perception_settings; + if (MLResult_Ok != MLPerceptionInitSettings(&perception_settings)) { + ML_LOG(Error, "%s: Failed to initialize perception.", application_name); + } + + if (MLResult_Ok != MLPerceptionStartup(&perception_settings)) { + ML_LOG(Error, "%s: Failed to startup perception.", application_name); + return -1; + } + + // Run the demo! + ML_LOG(Info, "%s: Begin demo.", application_name); + MLResult status = magicleap_pathfinder_demo(graphics_context.egl_display, graphics_context.egl_context); + ML_LOG(Info, "%s: End demo (%d).", application_name, status); + + // Shut down + MLPerceptionShutdown(); + + return 0; +} diff --git a/demo/magicleap/src/mocked_c_api.rs b/demo/magicleap/src/mocked_c_api.rs new file mode 100644 index 00000000..37159c68 --- /dev/null +++ b/demo/magicleap/src/mocked_c_api.rs @@ -0,0 +1,86 @@ +#![allow(unused_variables)] +#![allow(dead_code)] +#![allow(non_snake_case)] + +use crate::c_api::MLCoordinateFrameUID; +use crate::c_api::MLGraphicsClipExtentsInfoArray; +use crate::c_api::MLGraphicsFrameParams; +use crate::c_api::MLGraphicsOptions; +use crate::c_api::MLGraphicsRenderTargetsInfo; +use crate::c_api::MLGraphicsVirtualCameraInfoArray; +use crate::c_api::MLHandle; +use crate::c_api::MLHeadTrackingStaticData; +use crate::c_api::MLLogLevel; +use crate::c_api::MLResult; +use crate::c_api::MLSnapshotPtr; +use crate::c_api::MLTransform; +use std::os::raw::c_char; + +pub unsafe fn MLGraphicsCreateClientGL(options: *const MLGraphicsOptions, gl_context: MLHandle, graphics_client : &mut MLHandle) -> MLResult { + unimplemented!() +} + +pub unsafe fn MLGraphicsDestroyClient(graphics_client: *mut MLHandle) -> MLResult { + unimplemented!() +} + +pub unsafe fn MLHeadTrackingCreate(tracker: *mut MLHandle) -> MLResult { + unimplemented!() +} + +pub unsafe fn MLHeadTrackingGetStaticData(head_tracker: MLHandle, data: *mut MLHeadTrackingStaticData) -> MLResult { + unimplemented!() +} + +pub unsafe fn MLPerceptionGetSnapshot(snapshot: *mut MLSnapshotPtr) -> MLResult { + unimplemented!() +} + +pub unsafe fn MLSnapshotGetTransform(snapshot: MLSnapshotPtr, id: *const MLCoordinateFrameUID, transform: *mut MLTransform) -> MLResult { + unimplemented!() +} + +pub unsafe fn MLPerceptionReleaseSnapshot(snapshot: MLSnapshotPtr) -> MLResult { + unimplemented!() +} + +pub unsafe fn MLLifecycleSetReadyIndication() -> MLResult { + unimplemented!() +} + +pub unsafe fn MLGraphicsGetClipExtents(graphics_client: MLHandle, array: *mut MLGraphicsClipExtentsInfoArray) -> MLResult { + unimplemented!() +} + +pub unsafe fn MLGraphicsGetRenderTargets(graphics_client: MLHandle, targets: *mut MLGraphicsRenderTargetsInfo) -> MLResult { + unimplemented!() +} + +pub unsafe fn MLGraphicsInitFrameParams(params: *mut MLGraphicsFrameParams) -> MLResult { + unimplemented!() +} + +pub unsafe fn MLGraphicsBeginFrame(graphics_client: MLHandle, params: *const MLGraphicsFrameParams, frame_handle: *mut MLHandle, virtual_camera_array: *mut MLGraphicsVirtualCameraInfoArray) -> MLResult { + unimplemented!() +} + +pub unsafe fn MLGraphicsEndFrame(graphics_client: MLHandle, frame_handle: MLHandle) -> MLResult { + unimplemented!() +} + +pub unsafe fn MLGraphicsSignalSyncObjectGL(graphics_client: MLHandle, sync_object: MLHandle) -> MLResult { + unimplemented!() +} + +pub unsafe fn MLGetResultString(result_code: MLResult) -> *const c_char { + unimplemented!() +} + +pub unsafe fn MLLoggingLogLevelIsEnabled(lvl: MLLogLevel) -> bool { + unimplemented!() +} + +pub unsafe fn MLLoggingLog(lvl: MLLogLevel, tag: *const c_char, message: *const c_char) { + unimplemented!() +} +