Add magicleap demo

This commit is contained in:
Alan Jeffrey 2019-03-09 11:57:44 -06:00 committed by Josh Matthews
parent e00e862df0
commit 838bf684fb
16 changed files with 1501 additions and 0 deletions

View File

@ -2,6 +2,7 @@
members = [ members = [
"demo/android/rust", "demo/android/rust",
"demo/common", "demo/common",
"demo/magicleap",
"demo/native", "demo/native",
"geometry", "geometry",
"gl", "gl",

View File

@ -0,0 +1,3 @@
[target.aarch64-linux-android]
linker = "./fake-ld.sh"
ar = "aarch64-linux-android-ar"

1
demo/magicleap/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.out/

44
demo/magicleap/Cargo.toml Normal file
View File

@ -0,0 +1,44 @@
[package]
name = "pathfinder_immersive_demo"
version = "0.1.0"
edition = "2018"
authors = ["Alan Jeffrey <ajeffrey@mozilla.com>"]
[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"

View File

@ -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/

View File

@ -0,0 +1,2 @@
REFS = PathfinderDemo
OPTIONS=package/debuggable/on

18
demo/magicleap/fake-ld.sh Executable file
View File

@ -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}

View File

@ -0,0 +1,19 @@
<manifest
xmlns:ml="magicleap"
ml:package="com.mozilla.pathfinder.demo"
ml:version_code="1"
ml:version_name="1.0">
<application
ml:visible_name="Pathfinder Demo"
ml:sdk_version="0.19.0"
ml:min_api_level="3">
<uses-privilege ml:name="WorldReconstruction"/>
<uses-privilege ml:name="LowLatencyLightwear"/>
<component
ml:name=".fullscreen"
ml:visible_name="Pathfinder Demo"
ml:binary_name="bin/PathfinderDemo"
ml:type="Fullscreen">
</component>
</application>
</manifest>

281
demo/magicleap/src/c_api.rs Normal file
View File

@ -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<T> 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::*;

View File

@ -0,0 +1,46 @@
// pathfinder/demo/immersive/display.rs
//
// 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 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<Error = Self::Error>;
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<usvg::Error> + From<io::Error>{
}

View File

@ -0,0 +1,254 @@
// pathfinder/demo/immersive/glwindow.rs
//
// 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 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<GlWindow>,
running: bool,
cameras: Vec<GlWindowCamera>,
resource_loader: FilesystemResourceLoader,
}
pub struct GlWindowCamera {
eye: Eye,
gl_window: Rc<GlWindow>,
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<GlWindowDisplay, GlWindowError> {
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<CreationError> for GlWindowError {
fn from(err: CreationError) -> GlWindowError {
GlWindowError::Creation(err)
}
}
impl From<ContextError> for GlWindowError {
fn from(err: ContextError) -> GlWindowError {
GlWindowError::Context(err)
}
}
impl From<usvg::Error> for GlWindowError {
fn from(err: usvg::Error) -> GlWindowError {
GlWindowError::SVG(err)
}
}
impl From<io::Error> for GlWindowError {
fn from(err: io::Error) -> GlWindowError {
GlWindowError::IO(err)
}
}
impl DisplayError for GlWindowError {
}

View File

@ -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<D> {
display: D,
renderer: Renderer<GLDevice>,
scene_thread_proxy: SceneThreadProxy,
svg_size: Point2DF32,
svg_to_world: Option<Transform3DF32>,
}
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<D: Display> ImmersiveDemo<D> {
pub fn new(mut display: D) -> Result<Self, D::Error> {
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(())
}
}

57
demo/magicleap/src/lib.rs Normal file
View File

@ -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();
}
}

View File

@ -0,0 +1,362 @@
// pathfinder/demo/magicleap/magicleap.rs
//
// 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 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<Vec<Transform3DF32>>,
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
}
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(())
}
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<u32>) -> 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<Event> {
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, &params, &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, &params, &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<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])
}
}

179
demo/magicleap/src/main.cpp Normal file
View File

@ -0,0 +1,179 @@
#include <stdio.h>
#include <stdlib.h>
#include <chrono>
#include <cmath>
#ifndef EGL_EGLEXT_PROTOTYPES
#define EGL_EGLEXT_PROTOTYPES
#endif
#include <EGL/egl.h>
#include <EGL/eglext.h>
#ifndef GL_GLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES
#endif
#include <GLES3/gl3.h>
#include <GLES3/gl3ext.h>
#include <ml_graphics.h>
#include <ml_head_tracking.h>
#include <ml_perception.h>
#include <ml_lifecycle.h>
#include <ml_logging.h>
#include <ml_privileges.h>
// 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;
}

View File

@ -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!()
}