Add magicleap demo
This commit is contained in:
parent
e00e862df0
commit
838bf684fb
|
@ -2,6 +2,7 @@
|
|||
members = [
|
||||
"demo/android/rust",
|
||||
"demo/common",
|
||||
"demo/magicleap",
|
||||
"demo/native",
|
||||
"geometry",
|
||||
"gl",
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[target.aarch64-linux-android]
|
||||
linker = "./fake-ld.sh"
|
||||
ar = "aarch64-linux-android-ar"
|
|
@ -0,0 +1 @@
|
|||
.out/
|
|
@ -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"
|
|
@ -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/
|
|
@ -0,0 +1,2 @@
|
|||
REFS = PathfinderDemo
|
||||
OPTIONS=package/debuggable/on
|
|
@ -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}
|
|
@ -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>
|
|
@ -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::*;
|
|
@ -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>{
|
||||
}
|
|
@ -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 {
|
||||
}
|
|
@ -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(())
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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, ¶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<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])
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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!()
|
||||
}
|
||||
|
Loading…
Reference in New Issue