├── examples
└── room
│ ├── android
│ ├── gradle.properties
│ ├── .gitignore
│ ├── app
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── res
│ │ │ │ │ └── mipmap
│ │ │ │ │ │ └── servo.png
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ └── java
│ │ │ │ │ └── com
│ │ │ │ │ └── rust
│ │ │ │ │ └── webvr
│ │ │ │ │ └── MainActivity.java
│ │ │ ├── daydream
│ │ │ │ └── AndroidManifest.xml
│ │ │ └── gearvr
│ │ │ │ └── AndroidManifest.xml
│ │ └── build.gradle
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── settings.gradle
│ ├── build.gradle
│ ├── gradlew.bat
│ └── gradlew
│ ├── res
│ ├── sky.jpg
│ ├── floor.jpg
│ └── wall.jpg
│ ├── run_android.sh
│ ├── .cargo
│ └── config
│ ├── Cargo.toml
│ ├── fake-ld.sh
│ └── build.rs
├── rust-webvr
├── src
│ ├── api
│ │ ├── googlevr
│ │ │ ├── gradle
│ │ │ │ ├── settings.gradle
│ │ │ │ ├── gradle
│ │ │ │ │ └── wrapper
│ │ │ │ │ │ ├── gradle-wrapper.jar
│ │ │ │ │ │ └── gradle-wrapper.properties
│ │ │ │ ├── Makefile
│ │ │ │ ├── build.gradle
│ │ │ │ ├── src
│ │ │ │ │ └── main
│ │ │ │ │ │ ├── AndroidManifest.xml
│ │ │ │ │ │ └── java
│ │ │ │ │ │ └── com
│ │ │ │ │ │ └── rust
│ │ │ │ │ │ └── webvr
│ │ │ │ │ │ └── GVRService.java
│ │ │ │ ├── gradlew.bat
│ │ │ │ └── gradlew
│ │ │ ├── aar
│ │ │ │ └── GVRService.aar
│ │ │ ├── README.md
│ │ │ ├── mod.rs
│ │ │ ├── gamepad.rs
│ │ │ └── service.rs
│ │ ├── oculusvr
│ │ │ ├── gradle
│ │ │ │ ├── settings.gradle
│ │ │ │ ├── gradle
│ │ │ │ │ └── wrapper
│ │ │ │ │ │ ├── gradle-wrapper.jar
│ │ │ │ │ │ └── gradle-wrapper.properties
│ │ │ │ ├── src
│ │ │ │ │ └── main
│ │ │ │ │ │ ├── jniLibs
│ │ │ │ │ │ ├── arm64-v8a
│ │ │ │ │ │ │ └── libvrapi.so
│ │ │ │ │ │ └── armeabi-v7a
│ │ │ │ │ │ │ └── libvrapi.so
│ │ │ │ │ │ ├── assets
│ │ │ │ │ │ └── oculussig_ce0117115311a4160c
│ │ │ │ │ │ ├── AndroidManifest.xml
│ │ │ │ │ │ └── java
│ │ │ │ │ │ └── com
│ │ │ │ │ │ └── rust
│ │ │ │ │ │ └── webvr
│ │ │ │ │ │ └── OVRService.java
│ │ │ │ ├── build.gradle
│ │ │ │ ├── gradlew.bat
│ │ │ │ └── gradlew
│ │ │ ├── aar
│ │ │ │ └── OVRService.aar
│ │ │ ├── mod.rs
│ │ │ └── gamepad.rs
│ │ ├── glwindow
│ │ │ ├── mod.rs
│ │ │ ├── service.rs
│ │ │ ├── heartbeat.rs
│ │ │ └── display.rs
│ │ ├── magicleap
│ │ │ ├── magicleap_c_api.h
│ │ │ ├── mod.rs
│ │ │ ├── magicleap_c_api.rs
│ │ │ ├── service.rs
│ │ │ └── display.rs
│ │ ├── vrexternal
│ │ │ ├── other
│ │ │ │ └── mod.rs
│ │ │ ├── mod.rs
│ │ │ └── android
│ │ │ │ ├── mod.rs
│ │ │ │ ├── service.rs
│ │ │ │ └── mozgfx.rs
│ │ ├── openvr
│ │ │ ├── mod.rs
│ │ │ ├── library.rs
│ │ │ ├── gamepad.rs
│ │ │ └── service.rs
│ │ ├── mock
│ │ │ ├── mod.rs
│ │ │ ├── service.rs
│ │ │ └── display.rs
│ │ └── mod.rs
│ ├── lib.rs
│ └── vr_manager.rs
├── Cargo.toml
└── build.rs
├── rust-webvr-api
├── src
│ ├── vr_eye.rs
│ ├── vr_main_thread_heartbeat.rs
│ ├── vr_service.rs
│ ├── vr_stage_parameters.rs
│ ├── vr_field_view.rs
│ ├── mock.rs
│ ├── vr_display_data.rs
│ ├── vr_layer.rs
│ ├── vr_eye_parameters.rs
│ ├── vr_pose.rs
│ ├── vr_framebuffer.rs
│ ├── vr_display_capabilities.rs
│ ├── vr_future_frame_data.rs
│ ├── vr_gamepad.rs
│ ├── lib.rs
│ ├── vr_frame_data.rs
│ ├── vr_event.rs
│ ├── vr_display.rs
│ ├── jni_utils.rs
│ └── utils.rs
└── Cargo.toml
├── Cargo.toml
├── .gitignore
├── generate_aar.sh
├── .travis.yml
└── README.md
/examples/room/android/gradle.properties:
--------------------------------------------------------------------------------
1 | #android.useDeprecatedNdk=true
--------------------------------------------------------------------------------
/rust-webvr/src/api/googlevr/gradle/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'GVRService'
--------------------------------------------------------------------------------
/rust-webvr/src/api/oculusvr/gradle/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'OVRService'
--------------------------------------------------------------------------------
/rust-webvr-api/src/vr_eye.rs:
--------------------------------------------------------------------------------
1 | /// VREye
2 | pub enum VREye {
3 | Left,
4 | Right
5 | }
--------------------------------------------------------------------------------
/examples/room/res/sky.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/servo/rust-webvr/HEAD/examples/room/res/sky.jpg
--------------------------------------------------------------------------------
/examples/room/res/floor.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/servo/rust-webvr/HEAD/examples/room/res/floor.jpg
--------------------------------------------------------------------------------
/examples/room/res/wall.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/servo/rust-webvr/HEAD/examples/room/res/wall.jpg
--------------------------------------------------------------------------------
/examples/room/android/.gitignore:
--------------------------------------------------------------------------------
1 | /assets/
2 | /bin/
3 | /gen/
4 | /libs/
5 | /local.properties
6 | /proguard-project.txt
7 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = [
3 | "rust-webvr",
4 | "rust-webvr-api"
5 | ]
6 |
7 | exclude = [
8 | "examples"
9 | ]
--------------------------------------------------------------------------------
/rust-webvr/src/api/googlevr/aar/GVRService.aar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/servo/rust-webvr/HEAD/rust-webvr/src/api/googlevr/aar/GVRService.aar
--------------------------------------------------------------------------------
/rust-webvr/src/api/oculusvr/aar/OVRService.aar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/servo/rust-webvr/HEAD/rust-webvr/src/api/oculusvr/aar/OVRService.aar
--------------------------------------------------------------------------------
/examples/room/android/app/src/main/res/mipmap/servo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/servo/rust-webvr/HEAD/examples/room/android/app/src/main/res/mipmap/servo.png
--------------------------------------------------------------------------------
/examples/room/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/servo/rust-webvr/HEAD/examples/room/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | Cargo.lock
2 | target
3 | .vscode/
4 | .idea/
5 | .gradle/
6 | build/
7 |
8 | *.iml
9 | local.properties
10 |
11 | *~
12 |
13 | examples/room/android/captures/
14 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/googlevr/gradle/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/servo/rust-webvr/HEAD/rust-webvr/src/api/googlevr/gradle/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/rust-webvr/src/api/oculusvr/gradle/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/servo/rust-webvr/HEAD/rust-webvr/src/api/oculusvr/gradle/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/rust-webvr/src/api/glwindow/mod.rs:
--------------------------------------------------------------------------------
1 | mod display;
2 | mod service;
3 | mod heartbeat;
4 |
5 | pub use self::service::GlWindowVRService;
6 | pub use self::heartbeat::GlWindowVRMainThreadHeartbeat;
7 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/oculusvr/gradle/src/main/jniLibs/arm64-v8a/libvrapi.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/servo/rust-webvr/HEAD/rust-webvr/src/api/oculusvr/gradle/src/main/jniLibs/arm64-v8a/libvrapi.so
--------------------------------------------------------------------------------
/rust-webvr/src/api/oculusvr/gradle/src/main/jniLibs/armeabi-v7a/libvrapi.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/servo/rust-webvr/HEAD/rust-webvr/src/api/oculusvr/gradle/src/main/jniLibs/armeabi-v7a/libvrapi.so
--------------------------------------------------------------------------------
/rust-webvr/src/api/oculusvr/gradle/src/main/assets/oculussig_ce0117115311a4160c:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/servo/rust-webvr/HEAD/rust-webvr/src/api/oculusvr/gradle/src/main/assets/oculussig_ce0117115311a4160c
--------------------------------------------------------------------------------
/rust-webvr/src/api/magicleap/magicleap_c_api.h:
--------------------------------------------------------------------------------
1 | #include "ml_graphics.h"
2 | #include "ml_head_tracking.h"
3 | #include "ml_lifecycle.h"
4 | #include "ml_logging.h"
5 | #include "ml_perception.h"
6 | #include "ml_snapshot.h"
7 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/magicleap/mod.rs:
--------------------------------------------------------------------------------
1 | mod display;
2 | mod service;
3 | mod heartbeat;
4 | mod magicleap_c_api;
5 |
6 | pub use self::service::MagicLeapVRService;
7 | pub use self::heartbeat::MagicLeapVRMainThreadHeartbeat;
8 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/vrexternal/other/mod.rs:
--------------------------------------------------------------------------------
1 | use std::os::raw::c_void;
2 |
3 | pub struct VRExternalShmemPtr;
4 |
5 | impl VRExternalShmemPtr {
6 | pub fn new(_: *mut c_void) -> VRExternalShmemPtr {
7 | VRExternalShmemPtr
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/examples/room/run_android.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 | cargo build --target=arm-linux-androideabi --release
4 | cd ./android
5 | ./gradlew installGearvrArmRelease
6 | adb shell am start -n com.rust.webvr/com.rust.webvr.MainActivity
7 | #logcat-color | egrep '(RustAndroid|art|DEBUG)'
8 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/vrexternal/mod.rs:
--------------------------------------------------------------------------------
1 | #[cfg(target_os = "android")]
2 | mod android;
3 | #[cfg(target_os = "android")]
4 | pub use self::android::*;
5 |
6 | #[cfg(not(target_os = "android"))]
7 | mod other;
8 | #[cfg(not(target_os = "android"))]
9 | pub use self::other::*;
10 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/oculusvr/gradle/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
6 |
--------------------------------------------------------------------------------
/examples/room/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Mar 14 16:50:06 CET 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
7 |
--------------------------------------------------------------------------------
/examples/room/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/googlevr/gradle/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Nov 28 14:43:10 PST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
7 |
--------------------------------------------------------------------------------
/examples/room/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'RoomScaleDemo'
2 | include ':app'
3 | include ':GVRService'
4 | project(':GVRService').projectDir = new File('../../../rust-webvr/src/api/googlevr/gradle')
5 | include ':OVRService'
6 | project(':OVRService').projectDir = new File('../../../rust-webvr/src/api/oculusvr/gradle')
--------------------------------------------------------------------------------
/generate_aar.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | cd ./rust-webvr/src/api/googlevr/gradle
6 | ./gradlew assembleRelease
7 | cp ./build/outputs/aar/GVRService-release.aar ../aar/GVRService.aar
8 | cd -
9 |
10 | cd ./rust-webvr/src/api/oculusvr/gradle
11 | ./gradlew assembleRelease
12 | cp ./build/outputs/aar/OVRService-release.aar ../aar/OVRService.aar
13 | cd -
14 |
15 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: rust
2 |
3 | rust:
4 | - stable
5 |
6 | before_script:
7 | - rustup target add armv7-linux-androideabi
8 | - rustup target add x86_64-pc-windows-gnu
9 | - sudo apt-get install libc6-dev-i386
10 |
11 | script:
12 | - cargo check --target x86_64-pc-windows-gnu
13 | - cd rust-webvr && cargo check --features "mock oculusvr googlevr" --no-default-features --target=armv7-linux-androideabi
14 |
--------------------------------------------------------------------------------
/examples/room/.cargo/config:
--------------------------------------------------------------------------------
1 | [target.arm-linux-androideabi]
2 | linker = "./fake-ld.sh"
3 | ar = "arm-linux-androideabi-ar"
4 |
5 | [target.arm-unknown-linux-gnueabihf]
6 | linker = "arm-linux-gnueabihf-gcc"
7 | ar = "arm-linux-gnueabihf-ar"
8 |
9 | [target.aarch64-unknown-linux-gnu]
10 | linker = "aarch64-linux-gnu-gcc"
11 | ar = "aarch64-linux-gnu-ar"
12 |
13 | [target.'cfg(target_os=windows)']
14 | linker = "./fake-ld.cmd"
--------------------------------------------------------------------------------
/examples/room/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | jcenter()
5 | }
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:2.3.2'
8 | }
9 |
10 | buildDir = rootDir.absolutePath + "/../target/gradle"
11 | }
12 |
13 | allprojects {
14 | repositories {
15 | jcenter()
16 | }
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/examples/room/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "room_example"
3 | version = "0.1.0"
4 | authors = ["Imanol Fernandez "]
5 | build = "build.rs"
6 |
7 | [dependencies]
8 | glutin = "0.19"
9 | gleam = "0.4"
10 | cgmath = "0.12"
11 | image = "0.12"
12 | android_glue = "0.2"
13 | rust-webvr = { path = "../../rust-webvr" }
14 |
15 | [target.'cfg(target_os = "android")'.dependencies]
16 | android_injected_glue = {git = "https://github.com/mmatyas/android-rs-injected-glue"}
17 |
18 |
--------------------------------------------------------------------------------
/rust-webvr-api/src/vr_main_thread_heartbeat.rs:
--------------------------------------------------------------------------------
1 | /// Each VR service may have some code which needs to run on the main thread,
2 | /// for example window creation on MacOS is only supported on the main thread.
3 | /// Implementations of this trait will usually be neither `Sync` nor `Send`.
4 | pub trait VRMainThreadHeartbeat {
5 | /// Run the heartbeat on the main thread.
6 | fn heartbeat(&mut self);
7 |
8 | /// Is the heartbeat expecting to be called every frame?
9 | fn heart_racing(&self) -> bool;
10 | }
11 |
--------------------------------------------------------------------------------
/rust-webvr-api/src/vr_service.rs:
--------------------------------------------------------------------------------
1 | use VRDisplayPtr;
2 | use VREvent;
3 | use VRGamepadPtr;
4 |
5 | pub trait VRService: Send {
6 | fn initialize(&mut self) -> Result<(), String>;
7 |
8 | fn fetch_displays(&mut self) -> Result, String>;
9 |
10 | fn fetch_gamepads(&mut self) -> Result, String>;
11 |
12 | fn is_available(&self) -> bool;
13 |
14 | fn poll_events(&self) -> Vec;
15 | }
16 |
17 | pub trait VRServiceCreator {
18 | fn new_service(&self) -> Box;
19 | }
20 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/openvr/mod.rs:
--------------------------------------------------------------------------------
1 | mod binding;
2 | mod constants;
3 | mod display;
4 | mod library;
5 | mod gamepad;
6 | mod service;
7 |
8 | use {VRService, VRServiceCreator};
9 |
10 | pub struct OpenVRServiceCreator;
11 |
12 | impl OpenVRServiceCreator {
13 | pub fn new() -> Box {
14 | Box::new(OpenVRServiceCreator)
15 | }
16 | }
17 |
18 | impl VRServiceCreator for OpenVRServiceCreator {
19 |
20 | fn new_service(&self) -> Box {
21 | Box::new(service::OpenVRService::new())
22 | }
23 | }
--------------------------------------------------------------------------------
/rust-webvr/src/api/googlevr/gradle/Makefile:
--------------------------------------------------------------------------------
1 | SOURCES = \
2 | src/main/java/com/rust/webvr/GVRService.java \
3 | src/main/AndroidManifest.xml \
4 | build.gradle \
5 | settings.gradle \
6 | gradle/wrapper/gradle-wrapper.properties \
7 | gradle/wrapper/gradle-wrapper.jar
8 |
9 | OUTPUT_AAR = build/outputs/aar/GVRService-release.aar
10 | FINAL_AAR = ../aar/GVRService.aar
11 |
12 | all: $(FINAL_AAR)
13 |
14 | $(OUTPUT_AAR): $(SOURCES)
15 | ./gradlew assembleRelease && touch $(OUTPUT_AAR)
16 |
17 | $(FINAL_AAR): $(OUTPUT_AAR)
18 | cp $(OUTPUT_AAR) $(FINAL_AAR)
19 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/googlevr/README.md:
--------------------------------------------------------------------------------
1 |
2 | This module includes an Android Archive (`aar/GVRService.aar`). If you're modifying any of the Java/Android files under `gradle/` this file needs to be regenerated.
3 |
4 | This can be done by:
5 |
6 | - Opening `gradle/` as an Android Studio project
7 | - Setting the build variant to `release` (View -> Tool Windows -> Build Variants)
8 | - Running a build (Make Project, Ctrl-F9)
9 | - Copying `gradle/build/outputs/aar/GVRService.aar` to `aar/GVRService.aar` and checking it in
10 |
11 | The build can also be run standalone via `./gradleW assembleRelease`
--------------------------------------------------------------------------------
/rust-webvr-api/src/vr_stage_parameters.rs:
--------------------------------------------------------------------------------
1 | /// The VRStageParameters interface represents the values describing the
2 | /// stage/play area for displays that support room-scale experiences.
3 | #[derive(Debug, Clone)]
4 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
5 | pub struct VRStageParameters {
6 | /// matrix that transforms the sitting-space view matrices of VRFrameData to standing-space.
7 | pub sitting_to_standing_transform: [f32; 16],
8 | /// Width of the play-area bounds in meters.
9 | pub size_x: f32,
10 | /// Depth of the play-area bounds in meters
11 | pub size_z: f32
12 | }
--------------------------------------------------------------------------------
/rust-webvr/src/api/oculusvr/gradle/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/rust-webvr-api/src/vr_field_view.rs:
--------------------------------------------------------------------------------
1 | /// The VRFieldOfView interface represents a field of view,
2 | /// as given by 4 degrees describing the view from a center point.
3 |
4 | #[derive(Debug, Clone)]
5 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
6 | pub struct VRFieldOfView {
7 | pub up_degrees: f64,
8 | pub right_degrees: f64,
9 | pub down_degrees: f64,
10 | pub left_degrees: f64,
11 | }
12 |
13 | impl Default for VRFieldOfView {
14 | fn default() -> VRFieldOfView {
15 | VRFieldOfView {
16 | up_degrees: 0.0,
17 | right_degrees: 0.0,
18 | down_degrees: 0.0,
19 | left_degrees: 0.0
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/rust-webvr-api/src/mock.rs:
--------------------------------------------------------------------------------
1 |
2 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
3 | #[derive(Debug)]
4 | pub enum MockVRControlMsg {
5 | SetViewerPose([f32; 3], [f32; 4]),
6 | SetViews(MockVRView, MockVRView),
7 | SetEyeLevel(f32),
8 | Focus,
9 | Blur,
10 | }
11 |
12 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
13 | #[derive(Debug, Default)]
14 | pub struct MockVRInit {
15 | pub views: Option<(MockVRView, MockVRView)>,
16 | pub eye_level: Option,
17 | pub viewer_origin: Option<([f32; 3], [f32; 4])>,
18 | }
19 |
20 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
21 | #[derive(Debug)]
22 | pub struct MockVRView {
23 | pub projection: [f32; 16],
24 | pub offset: [f32; 3],
25 | }
26 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/googlevr/mod.rs:
--------------------------------------------------------------------------------
1 | #![cfg(feature = "googlevr")]
2 |
3 | mod display;
4 | mod gamepad;
5 | mod service;
6 |
7 | use {VRService, VRServiceCreator};
8 |
9 | pub struct GoogleVRServiceCreator;
10 |
11 | impl GoogleVRServiceCreator {
12 | pub fn new() -> Box {
13 | Box::new(GoogleVRServiceCreator)
14 | }
15 | }
16 |
17 | impl VRServiceCreator for GoogleVRServiceCreator {
18 | fn new_service(&self) -> Box {
19 | Box::new(service::GoogleVRService::new())
20 | }
21 | }
22 |
23 | // Export functions called from Java
24 | #[cfg(target_os="android")]
25 | pub mod jni {
26 | pub use super::service::Java_com_rust_webvr_GVRService_nativeOnPause;
27 | pub use super::service::Java_com_rust_webvr_GVRService_nativeOnResume;
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/oculusvr/gradle/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | jcenter()
4 | }
5 |
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:2.2.3'
8 | }
9 | }
10 |
11 | apply plugin: 'com.android.library'
12 |
13 | android {
14 | compileSdkVersion 25
15 | buildToolsVersion "25.0.2"
16 |
17 | defaultConfig {
18 | minSdkVersion 21
19 | targetSdkVersion 25
20 | versionCode 1
21 | versionName "1.0.0"
22 | }
23 | buildTypes {
24 | release {
25 | minifyEnabled false
26 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
27 | }
28 | }
29 |
30 | repositories {
31 | jcenter()
32 | }
33 |
34 | dependencies {
35 |
36 | }
37 | }
--------------------------------------------------------------------------------
/rust-webvr/src/api/mock/mod.rs:
--------------------------------------------------------------------------------
1 | mod display;
2 | mod service;
3 |
4 | pub use {VRService, VRServiceCreator, VREyeParameters, VRStageParameters, MockVRControlMsg, MockVRInit, MockVRView};
5 | use std::sync::mpsc::{channel, Sender};
6 |
7 | pub struct MockServiceCreator;
8 |
9 | impl MockServiceCreator {
10 | pub fn new() -> Box {
11 | Box::new(MockServiceCreator)
12 | }
13 |
14 | pub fn new_service_with_remote(init: MockVRInit) -> (Box, Sender) {
15 | let (send, rcv) = channel();
16 | let service = service::MockVRService::new_with_receiver(rcv, init);
17 | (Box::new(service), send)
18 | }
19 | }
20 |
21 | impl VRServiceCreator for MockServiceCreator {
22 | fn new_service(&self) -> Box {
23 | Box::new(service::MockVRService::new(Default::default()))
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/oculusvr/mod.rs:
--------------------------------------------------------------------------------
1 | #![cfg(target_os="android")]
2 | #![cfg(feature = "oculusvr")]
3 |
4 | mod display;
5 | mod gamepad;
6 | mod service;
7 |
8 | use {VRService, VRServiceCreator};
9 |
10 | pub struct OculusVRServiceCreator;
11 |
12 | impl OculusVRServiceCreator {
13 | pub fn new() -> Box {
14 | Box::new(OculusVRServiceCreator)
15 | }
16 | }
17 |
18 | impl VRServiceCreator for OculusVRServiceCreator {
19 | fn new_service(&self) -> Box {
20 | Box::new(service::OculusVRService::new())
21 | }
22 | }
23 |
24 | // Export functions called from Java
25 | pub mod jni {
26 | pub use super::service::Java_com_rust_webvr_OVRService_nativeOnPause;
27 | pub use super::service::Java_com_rust_webvr_OVRService_nativeOnResume;
28 | pub use super::service::Java_com_rust_webvr_OVRService_nativeOnSurfaceChanged;
29 | pub use super::service::Java_com_rust_webvr_OVRService_nativeOnSurfaceDestroyed;
30 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # rust-webvr
2 | Safe rust API that provides a way to interact with Virtual Reality headsets and integration with vendor specific SDKs like OpenVR, Oculus and GoogleVR (Daydream). The API is inspired on the easy to use WebVR API but adapted to Rust design patterns.
3 |
4 | It's used in the WebVR Core implementation for Servo browser. This module can be tested outside of Servo and even be used on any vanilla Rust app.
5 |
6 | ## Room Scale example:
7 |
8 | Just run this command in examples/room folder
9 |
10 | ```
11 | cargo run
12 | ```
13 |
14 | Run room scale demo on android:
15 |
16 | ```
17 | ./run_android.sh
18 | ```
19 |
20 | ### OpenVR tips:
21 |
22 | In order to run with openvr on windows, `openvr_api.dll` must be available. Please make it either accessible in your path, or copy it into the examples/room folder.
23 |
24 | Refer to [The ValveSoftware openvr repository](https://github.com/ValveSoftware/openvr) and head over to the releases section for more information.
25 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/magicleap/magicleap_c_api.rs:
--------------------------------------------------------------------------------
1 | #![allow(dead_code)]
2 | #![allow(non_upper_case_globals)]
3 | #![allow(non_camel_case_types)]
4 |
5 | use std::ffi::CStr;
6 |
7 | #[repr(transparent)]
8 | #[derive(Clone, Copy, Debug, Eq, PartialEq)]
9 | pub struct MLResult(u32);
10 |
11 | impl MLResult {
12 | pub const Ok: Self = MLResult(MLResultGlobal_MLResult_Ok);
13 | pub const Timeout: Self = MLResult(MLResultGlobal_MLResult_Timeout);
14 | pub const UnspecifiedFailure: Self = MLResult(MLResultGlobal_MLResult_UnspecifiedFailure);
15 |
16 | pub fn ok(self) -> Result<(), MLResult> {
17 | if self == Self::Ok {
18 | Ok(())
19 | } else {
20 | Err(self)
21 | }
22 | }
23 | }
24 |
25 | impl From for String {
26 | fn from(result: MLResult) -> String {
27 | let cstr = unsafe { CStr::from_ptr(MLSnapshotGetResultString(result)) };
28 | cstr.to_string_lossy().into_owned()
29 | }
30 | }
31 |
32 | include!(concat!(env!("OUT_DIR"), "/magicleap_c_api.rs"));
--------------------------------------------------------------------------------
/rust-webvr-api/src/vr_display_data.rs:
--------------------------------------------------------------------------------
1 | use VRDisplayCapabilities;
2 | use VREyeParameters;
3 | use VRStageParameters;
4 |
5 | #[derive(Debug, Clone)]
6 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
7 | pub struct VRDisplayData {
8 | pub display_id: u32,
9 | pub display_name: String,
10 | pub connected: bool,
11 | pub capabilities: VRDisplayCapabilities,
12 | pub stage_parameters: Option,
13 | pub left_eye_parameters: VREyeParameters,
14 | pub right_eye_parameters: VREyeParameters,
15 | }
16 |
17 | impl Default for VRDisplayData {
18 | fn default() -> VRDisplayData {
19 | VRDisplayData {
20 | display_id: 0,
21 | display_name: String::new(),
22 | connected: false,
23 | capabilities: VRDisplayCapabilities::default(),
24 | stage_parameters: None,
25 | left_eye_parameters: VREyeParameters::default(),
26 | right_eye_parameters: VREyeParameters::default()
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/rust-webvr/src/api/vrexternal/android/mod.rs:
--------------------------------------------------------------------------------
1 | mod display;
2 | mod mozgfx;
3 | mod service;
4 |
5 | use std::os::raw::c_void;
6 | use {VRService, VRServiceCreator};
7 |
8 | #[derive(Clone)]
9 | pub struct VRExternalShmemPtr(*mut mozgfx::VRExternalShmem);
10 |
11 | unsafe impl Send for VRExternalShmemPtr {}
12 | unsafe impl Sync for VRExternalShmemPtr {}
13 |
14 | impl VRExternalShmemPtr {
15 | fn as_mut(&self) -> &mut mozgfx::VRExternalShmem {
16 | unsafe { &mut *(self.0) }
17 | }
18 |
19 | pub fn new(raw: *mut c_void) -> VRExternalShmemPtr {
20 | VRExternalShmemPtr(raw as *mut mozgfx::VRExternalShmem)
21 | }
22 | }
23 |
24 | pub struct VRExternalServiceCreator(VRExternalShmemPtr);
25 |
26 | impl VRExternalServiceCreator {
27 | pub fn new(ptr: VRExternalShmemPtr) -> Box {
28 | Box::new(VRExternalServiceCreator(ptr))
29 | }
30 | }
31 |
32 | impl VRServiceCreator for VRExternalServiceCreator {
33 | fn new_service(&self) -> Box {
34 | Box::new(service::VRExternalService::new(self.0.clone()))
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/rust-webvr-api/src/vr_layer.rs:
--------------------------------------------------------------------------------
1 | /// Data provided to a VRDisplay and presented in the HMD.
2 | #[derive(Debug, Clone)]
3 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
4 | pub struct VRLayer {
5 | /// Source texture whose contents will be presented by the
6 | /// VRDisplay when VRDisplay.submitFrame() is called.
7 | pub texture_id: u32,
8 |
9 | /// UVs defining the texture bounds to present to the eye in UV space: [x,y,w,h]
10 | /// Defaults to [0.0, 0.0, 0.5, 1.0]
11 | pub left_bounds: [f32; 4],
12 |
13 | /// UVs defining the texture bounds to present to the eye in UV space: [x,y,w,h]
14 | /// Defaults to [0.5, 0.0, 0.5, 1.0]
15 | pub right_bounds: [f32; 4],
16 |
17 | /// Hint with texture size
18 | pub texture_size: Option<(u32, u32)>,
19 | }
20 |
21 | impl Default for VRLayer {
22 | fn default() -> VRLayer {
23 | VRLayer {
24 | texture_id: 0,
25 | left_bounds: [0.0, 0.0, 0.5, 1.0],
26 | right_bounds: [0.5, 0.0, 0.5, 1.0],
27 | texture_size : None
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/rust-webvr/src/api/googlevr/gradle/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | jcenter()
4 | maven {
5 | url 'https://maven.google.com/'
6 | name 'Google'
7 | }
8 | }
9 |
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:3.1.3'
12 | }
13 | }
14 |
15 | apply plugin: 'com.android.library'
16 |
17 | android {
18 | compileSdkVersion 25
19 | buildToolsVersion "27.0.3"
20 |
21 | defaultConfig {
22 | minSdkVersion 21
23 | targetSdkVersion 25
24 | versionCode 1
25 | versionName "1.0.0"
26 | }
27 | buildTypes {
28 | release {
29 | minifyEnabled false
30 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
31 | }
32 | }
33 |
34 | repositories {
35 | jcenter()
36 | }
37 |
38 | dependencies {
39 | implementation 'com.google.vr:sdk-base:1.120.0'
40 | }
41 | }
42 |
43 | repositories {
44 | maven {
45 | url 'https://maven.google.com/'
46 | name 'Google'
47 | }
48 | }
--------------------------------------------------------------------------------
/rust-webvr-api/src/vr_eye_parameters.rs:
--------------------------------------------------------------------------------
1 | use VRFieldOfView;
2 |
3 | /// The VREyeParameters interface represents all the information
4 | /// required to correctly render a scene for a given eye.
5 | #[derive(Debug, Clone)]
6 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
7 | pub struct VREyeParameters {
8 | /// Offset from the center point between the users eyes to the center of the eye in meters.
9 | pub offset: [f32; 3],
10 |
11 | /// Describes the recommended render target width of each eye viewport, in pixels.
12 | pub render_width: u32,
13 |
14 | /// Describes the recommended render target height of each eye viewport, in pixels.
15 | pub render_height: u32,
16 |
17 | /// Describes the current field of view for the eye
18 | pub field_of_view: VRFieldOfView
19 | }
20 |
21 | impl Default for VREyeParameters {
22 | fn default() -> VREyeParameters {
23 | VREyeParameters {
24 | offset: [0.0, 0.0, 0.0],
25 | render_width: 0,
26 | render_height: 0,
27 | field_of_view: VRFieldOfView::default()
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/rust-webvr-api/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "rust-webvr-api"
3 | version = "0.17.0"
4 | authors = ["The Servo Project Developers"]
5 |
6 | homepage = "https://github.com/servo/rust-webvr"
7 | repository = "https://github.com/servo/rust-webvr"
8 | keywords = ["webvr", "openvr", "oculus", "headset", "vr"]
9 | license = "MPL-2.0"
10 |
11 | description = '''Safe rust API that provides a way to interact with Virtual Reality headsets
12 | and integration with vendor specific SDKs like OpenVR and Oculus. The API is inspired on the
13 | easy to use WebVR API but adapted to Rust design patterns'''
14 |
15 | [features]
16 | default = ["utils", "jni_utils"]
17 | utils = ["time"]
18 | jni_utils = ["android_injected_glue"]
19 | serde-serialization = ["serde", "serde_derive"]
20 | ipc = ["serde-serialization", "ipc-channel"]
21 |
22 | [dependencies]
23 | ipc-channel = { version = "0.14", optional = true }
24 | serde = { version = "1.0", optional = true }
25 | serde_derive = { version = "1.0", optional = true }
26 | sparkle = "0.1"
27 | time = { version = "0.1", optional = true }
28 |
29 | [target.'cfg(target_os = "android")'.dependencies]
30 | android_injected_glue = { version = "0.2.2", optional = true }
31 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/googlevr/gradle/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/room/fake-ld.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This Source Code Form is subject to the terms of the Mozilla Public
4 | # License, v. 2.0. If a copy of the MPL was not distributed with this
5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 | set -o errexit
8 | set -o nounset
9 | set -o pipefail
10 |
11 | TARGET_DIR="${OUT_DIR}/../../../libs/armeabi"
12 | mkdir -p $TARGET_DIR
13 |
14 | export _ANDROID_ARCH=arch-arm
15 | export ANDROID_SYSROOT="${ANDROID_NDK}/platforms/android-18/${_ANDROID_ARCH}"
16 | export _ANDROID_EABI=arm-linux-androideabi-4.9
17 | ANDROID_TOOLCHAIN=""
18 | for host in "linux-x86_64" "linux-x86" "darwin-x86_64" "darwin-x86"; do
19 | if [[ -d "${ANDROID_NDK}/toolchains/${_ANDROID_EABI}/prebuilt/${host}/bin" ]]; then
20 | ANDROID_TOOLCHAIN="${ANDROID_NDK}/toolchains/${_ANDROID_EABI}/prebuilt/${host}/bin"
21 | break
22 | fi
23 | done
24 |
25 | ANDROID_CPU_ARCH_DIR=armeabi
26 | ANDROID_CXX_LIBS="${ANDROID_NDK}/sources/cxx-stl/llvm-libc++/libs/${ANDROID_CPU_ARCH_DIR}"
27 |
28 | echo "toolchain: ${ANDROID_TOOLCHAIN}"
29 | echo "libs dir: ${ANDROID_CXX_LIBS}"
30 | echo "sysroot: ${ANDROID_SYSROOT}"
31 |
32 | "${ANDROID_TOOLCHAIN}/arm-linux-androideabi-gcc" \
33 | --sysroot="${ANDROID_SYSROOT}" -L "${ANDROID_CXX_LIBS}" "${@}" -lc++ \
34 | -o "${TARGET_DIR}/libwebvr.so" -shared && touch "${TARGET_DIR}/webvr"
35 |
--------------------------------------------------------------------------------
/rust-webvr-api/src/vr_pose.rs:
--------------------------------------------------------------------------------
1 | /// The VRPose struct represents a sensor’s state at a given timestamp.
2 | #[derive(Debug, Clone, Copy, Default)]
3 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
4 | pub struct VRPose {
5 | /// Position of the VRDisplay as a 3D vector.
6 | /// May be None if the sensor is incapable of providing positional data.
7 | pub position: Option<[f32; 3]>,
8 |
9 | /// Linear velocity of the sensor given in meters per second.
10 | /// May be None if the sensor is incapable of providing linear velocity data.
11 | pub linear_velocity: Option<[f32; 3]>,
12 |
13 | /// Linear acceleration of the sensor given in meters per second squared.
14 | /// May be None if the sensor is incapable of providing linear acceleration data.
15 | pub linear_acceleration: Option<[f32; 3]>,
16 |
17 | /// Orientation of the sensor as a quaternion.
18 | /// May be None if the sensor is incapable of providing orientation.
19 | pub orientation: Option<[f32; 4]>,
20 |
21 | /// Angular velocity of the sensor given in radians per second.
22 | /// May be None if the sensor is incapable of providing angular velocity data.
23 | pub angular_velocity: Option<[f32; 3]>,
24 |
25 | /// Linear acceleration of the sensor given in radians per second squared.
26 | /// May be None if the sensor is incapable of providing angular acceleration data.
27 | pub angular_acceleration: Option<[f32; 3]>,
28 | }
--------------------------------------------------------------------------------
/rust-webvr/src/api/mod.rs:
--------------------------------------------------------------------------------
1 | #[cfg(feature = "vrexternal")]
2 | mod vrexternal;
3 | #[cfg(feature = "vrexternal")]
4 | pub use self::vrexternal::VRExternalShmemPtr;
5 | #[cfg(all(feature = "vrexternal", target_os= "android"))]
6 | pub use self::vrexternal::VRExternalServiceCreator;
7 |
8 | #[cfg(feature = "mock")]
9 | mod mock;
10 | #[cfg(feature = "mock")]
11 | pub use self::mock::{MockServiceCreator, MockVRControlMsg, MockVRInit};
12 |
13 | #[cfg(feature = "glwindow")]
14 | mod glwindow;
15 | #[cfg(feature = "glwindow")]
16 | pub use self::glwindow::GlWindowVRService;
17 |
18 | #[cfg(feature = "magicleap")]
19 | mod magicleap;
20 | #[cfg(feature = "magicleap")]
21 | pub use self::magicleap::MagicLeapVRService;
22 |
23 | #[cfg(all(target_os="windows", feature = "openvr"))]
24 | mod openvr;
25 | #[cfg(all(target_os="windows", feature = "openvr"))]
26 | pub use self::openvr::OpenVRServiceCreator;
27 |
28 | #[cfg(all(feature = "googlevr", target_os= "android"))]
29 | mod googlevr;
30 | #[cfg(all(feature = "googlevr", target_os= "android"))]
31 | pub use self::googlevr::GoogleVRServiceCreator;
32 | #[cfg(all(feature = "googlevr", target_os= "android"))]
33 | pub use self::googlevr::jni::*;
34 |
35 | #[cfg(all(feature = "oculusvr", target_os= "android"))]
36 | mod oculusvr;
37 | #[cfg(all(feature = "oculusvr", target_os= "android"))]
38 | pub use self::oculusvr::OculusVRServiceCreator;
39 | #[cfg(all(feature = "oculusvr", target_os= "android"))]
40 | pub use self::oculusvr::jni::*;
41 |
--------------------------------------------------------------------------------
/rust-webvr-api/src/vr_framebuffer.rs:
--------------------------------------------------------------------------------
1 | /// Information about a FBO provided by a VRDisplay.
2 | #[derive(Debug, Clone)]
3 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
4 | pub struct VRFramebuffer {
5 | /// Eye index of the framebuffer
6 | pub eye_index: u32,
7 |
8 | /// The attributes set up for this framebuffer
9 | pub attributes: VRFramebufferAttributes,
10 |
11 | /// The 2D rectangle that should be used to project the 3D scene
12 | /// to the position of the eye camera. Measured in device pixels.
13 | pub viewport: VRViewport,
14 | }
15 |
16 | #[derive(Debug, Copy, Clone)]
17 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
18 | pub struct VRFramebufferAttributes {
19 | pub multiview: bool,
20 | pub depth: bool,
21 | pub multisampling: bool,
22 | }
23 |
24 | impl Default for VRFramebufferAttributes {
25 | fn default() -> VRFramebufferAttributes {
26 | Self {
27 | multiview: false,
28 | depth: false,
29 | multisampling: false,
30 | }
31 | }
32 | }
33 |
34 | #[derive(Debug, Clone)]
35 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
36 | pub struct VRViewport {
37 | pub x: i32,
38 | pub y: i32,
39 | pub width: i32,
40 | pub height: i32,
41 | }
42 |
43 | impl VRViewport {
44 | pub fn new(x: i32, y: i32, width: i32, height: i32) -> Self {
45 | Self {
46 | x: x,
47 | y: y,
48 | width: width,
49 | height: height,
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/rust-webvr/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "rust-webvr"
3 | version = "0.19.0"
4 | authors = ["The Servo Project Developers"]
5 |
6 | homepage = "https://github.com/servo/rust-webvr"
7 | repository = "https://github.com/servo/rust-webvr"
8 | keywords = ["webvr", "openvr", "oculus", "headset", "vr"]
9 | license = "MPL-2.0"
10 |
11 | description = '''Safe rust API that provides a way to interact with Virtual Reality headsets
12 | and integration with vendor specific SDKs like OpenVR and Oculus. The API is inspired on the
13 | easy to use WebVR API but adapted to Rust design patterns'''
14 |
15 | exclude = [
16 | "examples/*",
17 | ]
18 |
19 | build = "build.rs"
20 |
21 | [features]
22 | default = ["vrexternal", "openvr", "mock"]
23 | vrexternal = []
24 | glwindow = ["euclid", "gleam", "sparkle", "glutin"]
25 | openvr = ["libloading"]
26 | mock = []
27 | googlevr = ["gvr-sys"]
28 | oculusvr = ["ovr-mobile-sys"]
29 | magicleap = ["euclid", "gleam", "sparkle"]
30 |
31 | [dependencies]
32 | rust-webvr-api = { path = "../rust-webvr-api", version = "0.17" }
33 | log = "0.4"
34 | gvr-sys = { version = "0.7", optional = true }
35 | ovr-mobile-sys = { version = "0.4", optional = true }
36 | libc = "0.2"
37 | euclid = { version = "0.20", optional = true }
38 | gleam = { version = "0.9", optional = true }
39 | glutin = { version = "0.21", optional = true }
40 | sparkle = { version = "0.1", optional = true }
41 |
42 | [target.'cfg(target_os="windows")'.dependencies]
43 | libloading = { version = "0.5", optional = true, default-features = false }
44 |
45 | [build-dependencies]
46 | gl_generator = "0.13"
47 | bindgen = "0.53"
48 |
--------------------------------------------------------------------------------
/examples/room/android/app/src/daydream/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
19 |
20 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/openvr/library.rs:
--------------------------------------------------------------------------------
1 | use libloading as lib;
2 | #[cfg(unix)]
3 | use libloading::os::unix::Symbol as Symbol;
4 | #[cfg(windows)]
5 | use libloading::os::windows::Symbol as Symbol;
6 |
7 | use super::binding as openvr;
8 |
9 | // OpenVR_api.dll entry points
10 | type VRInitInternal = unsafe extern fn(*mut openvr::EVRInitError, openvr::EVRApplicationType) -> isize;
11 | type VRShutdownInternal = unsafe extern fn();
12 | type VRIsHmdPresent = unsafe extern fn() -> bool;
13 | type VRGetGenericInterface = unsafe extern fn(*const ::std::os::raw::c_char, *mut openvr::EVRInitError) -> isize;
14 |
15 | pub struct OpenVRLibrary {
16 | _lib: lib::Library,
17 | pub init_internal: Symbol,
18 | pub shutdown_internal: Symbol,
19 | pub is_hmd_present: Symbol,
20 | pub get_interface: Symbol
21 | }
22 |
23 | impl OpenVRLibrary {
24 | pub unsafe fn new()-> lib::Result {
25 | let lib = try!(lib::Library::new("openvr_api.dll"));
26 | let init_internal = try!(lib.get::(b"VR_InitInternal\0")).into_raw();
27 | let shutdown_internal = try!(lib.get::(b"VR_ShutdownInternal\0")).into_raw();
28 | let is_hmd_present = try!(lib.get::(b"VR_IsHmdPresent\0")).into_raw();
29 | let get_interface = try!(lib.get::(b"VR_GetGenericInterface\0")).into_raw();
30 |
31 | Ok(OpenVRLibrary {
32 | _lib: lib,
33 | init_internal: init_internal,
34 | shutdown_internal: shutdown_internal,
35 | is_hmd_present: is_hmd_present,
36 | get_interface: get_interface
37 | })
38 | }
39 | }
--------------------------------------------------------------------------------
/rust-webvr-api/src/vr_display_capabilities.rs:
--------------------------------------------------------------------------------
1 | /// describes the capabilities of a VRDisplay. These are expected to be static per-device/per-user.
2 | #[derive(Debug, Clone)]
3 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
4 | pub struct VRDisplayCapabilities {
5 | /// true if the VRDisplay is capable of tracking its position.
6 | pub has_position: bool,
7 |
8 | /// true if the VRDisplay is capable of tracking its orientation.
9 | pub has_orientation: bool,
10 |
11 | /// true if the VRDisplay is separate from the device’s primary display
12 | pub has_external_display: bool,
13 |
14 | /// true if the VRDisplay is capable of presenting content to an HMD or similar device.
15 | pub can_present: bool,
16 |
17 | /// true if the VR display expects the browser to present the content.
18 | /// This is now deprecated, a better solution is to implement `future_frame_data`
19 | /// and have the future resolve when the next animation frame is ready.
20 | #[deprecated(since="0.10.3", note="please use `future_frame_data` instead")]
21 | pub presented_by_browser: bool,
22 |
23 | /// Indicates the maximum length of the array that requestPresent() will accept,
24 | /// Must be 1 if canPresent is true, 0 otherwise.
25 | pub max_layers: u64
26 | }
27 |
28 | impl Default for VRDisplayCapabilities {
29 | fn default() -> VRDisplayCapabilities {
30 | #[allow(deprecated)]
31 | VRDisplayCapabilities {
32 | has_position: false,
33 | has_orientation: false,
34 | has_external_display: false,
35 | can_present: false,
36 | presented_by_browser: false,
37 | max_layers: 0
38 | }
39 | }
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/vrexternal/android/service.rs:
--------------------------------------------------------------------------------
1 | use super::display::{VRExternalDisplay, VRExternalDisplayPtr};
2 | use super::VRExternalShmemPtr;
3 | use {VRDisplayPtr, VREvent, VRGamepadPtr, VRService};
4 |
5 | pub struct VRExternalService {
6 | shmem: VRExternalShmemPtr,
7 | display: Option,
8 | }
9 |
10 | unsafe impl Send for VRExternalService {}
11 |
12 | impl VRService for VRExternalService {
13 | fn initialize(&mut self) -> Result<(), String> {
14 | Ok(())
15 | }
16 |
17 | fn fetch_displays(&mut self) -> Result, String> {
18 | if self.display.is_none() {
19 | // Block until enumerationCompleted is true.
20 | self.shmem.as_mut().pull_system(&|state| state.enumerationCompleted);
21 | let display = VRExternalDisplay::new(self.shmem.clone());
22 | self.display = Some(display);
23 | }
24 | Ok(vec![self.display.as_ref().unwrap().clone()])
25 | }
26 |
27 | fn fetch_gamepads(&mut self) -> Result, String> {
28 | Ok(Vec::new())
29 | }
30 |
31 | fn is_available(&self) -> bool {
32 | true
33 | }
34 |
35 | fn poll_events(&self) -> Vec {
36 | match &self.display {
37 | None => vec![],
38 | Some(display) => {
39 | display.borrow_mut()
40 | .poll_events()
41 | .into_iter()
42 | .map(|e| VREvent::Display(e))
43 | .collect()
44 | }
45 | }
46 | }
47 | }
48 |
49 | impl VRExternalService {
50 | pub fn new(ptr: VRExternalShmemPtr) -> VRExternalService {
51 | VRExternalService {
52 | shmem: ptr.clone(),
53 | display: None,
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/examples/room/android/app/src/gearvr/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
21 |
22 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/mock/service.rs:
--------------------------------------------------------------------------------
1 | use {VRService, VRDisplayPtr, VREvent, VRGamepadPtr};
2 | use super::display::{MockVRDisplay, MockVRDisplayPtr};
3 | use super::{MockVRControlMsg, MockVRInit};
4 | use std::thread;
5 | use std::sync::mpsc::Receiver;
6 |
7 | pub struct MockVRService {
8 | display: MockVRDisplayPtr,
9 | }
10 |
11 | unsafe impl Send for MockVRService {}
12 |
13 | impl VRService for MockVRService {
14 | fn initialize(&mut self) -> Result<(), String> {
15 | Ok(())
16 | }
17 |
18 | fn fetch_displays(&mut self) -> Result,String> {
19 | Ok(vec![self.display.clone()])
20 | }
21 |
22 | fn fetch_gamepads(&mut self) -> Result,String> {
23 | Ok(Vec::new())
24 | }
25 |
26 | fn is_available(&self) -> bool {
27 | true
28 | }
29 |
30 | fn poll_events(&self) -> Vec {
31 | self.display.borrow().poll_events()
32 | }
33 | }
34 |
35 | impl MockVRService {
36 | pub fn new(init: MockVRInit) -> MockVRService {
37 | MockVRService {
38 | display: MockVRDisplay::new(init),
39 | }
40 | }
41 |
42 | pub fn new_with_receiver(rcv: Receiver, init: MockVRInit) -> MockVRService {
43 | let display = MockVRDisplay::new(init);
44 | let state = display.borrow().state_handle();
45 | thread::spawn(move || {
46 | while let Ok(msg) = rcv.recv() {
47 | // The only reason we need this is that the overall display API
48 | // is somewhat unsound:
49 | // https://github.com/servo/rust-webvr/issues/18 .
50 | // Once that is fixed we should just have a handle to the display here
51 | state.lock().unwrap().handle_msg(msg);
52 | }
53 | });
54 | MockVRService {
55 | display,
56 | }
57 | }
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/rust-webvr/src/lib.rs:
--------------------------------------------------------------------------------
1 | #[cfg(all(feature = "vrexternal", target_os= "android"))]
2 | extern crate libc;
3 | extern crate rust_webvr_api;
4 | #[cfg(all(feature = "googlevr", target_os= "android"))]
5 | extern crate gvr_sys;
6 | #[cfg(all(target_os="windows", feature = "openvr"))]
7 | extern crate libloading;
8 | #[macro_use]
9 | extern crate log;
10 | #[cfg(all(feature = "oculusvr", target_os= "android"))]
11 | extern crate ovr_mobile_sys;
12 | #[cfg(any(feature = "magicleap", feature = "glwindow"))]
13 | extern crate euclid;
14 | #[cfg(any(feature = "magicleap", feature = "glwindow"))]
15 | extern crate gleam;
16 | #[cfg(feature = "glwindow")]
17 | extern crate glutin;
18 | #[cfg(feature = "serde-serialization")]
19 | #[macro_use] extern crate serde_derive;
20 | #[cfg(any(feature = "magicleap", feature = "glwindow"))]
21 | extern crate sparkle;
22 |
23 | #[cfg(any(feature = "googlevr", feature= "oculusvr"))]
24 | mod gl {
25 | include!(concat!(env!("OUT_DIR"), "/gles_bindings.rs"));
26 | }
27 |
28 | #[cfg(any(feature = "magicleap", feature = "oculusvr"))]
29 | mod egl {
30 | #![allow(non_camel_case_types, non_snake_case)]
31 | use std::os::raw::{c_long, c_void};
32 | pub type khronos_utime_nanoseconds_t = khronos_uint64_t;
33 | pub type khronos_uint64_t = u64;
34 | pub type khronos_ssize_t = c_long;
35 | pub type EGLint = i32;
36 | pub type EGLNativeDisplayType = *const c_void;
37 | pub type EGLNativePixmapType = *const c_void;
38 | pub type EGLNativeWindowType = *const c_void;
39 | pub type NativeDisplayType = EGLNativeDisplayType;
40 | pub type NativePixmapType = EGLNativePixmapType;
41 | pub type NativeWindowType = EGLNativeWindowType;
42 | include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs"));
43 | }
44 |
45 | #[cfg(feature= "glwindow")]
46 | pub use api::GlWindowVRService;
47 |
48 | pub mod api;
49 | mod vr_manager;
50 |
51 | pub use rust_webvr_api::*;
52 | pub use vr_manager::VRServiceManager;
53 |
--------------------------------------------------------------------------------
/rust-webvr-api/src/vr_future_frame_data.rs:
--------------------------------------------------------------------------------
1 | use vr_frame_data::VRFrameData;
2 |
3 | #[cfg(not(feature = "ipc"))]
4 | use std::sync::mpsc::{channel, Sender, Receiver};
5 |
6 | #[cfg(feature = "ipc")]
7 | use ipc_channel::ipc::{channel as ipc_channel, IpcSender as Sender, IpcReceiver as Receiver};
8 |
9 | #[cfg(feature = "ipc")]
10 | fn channel() -> (Sender, Receiver) where
11 | T: for<'de> serde::de::Deserialize<'de> + serde::ser::Serialize,
12 | {
13 | ipc_channel().expect("Failed to create IPC channel")
14 | }
15 |
16 | #[cfg_attr(feature = "ipc", derive(Serialize, Deserialize))]
17 | enum State {
18 | Resolved(T),
19 | Blocked(U),
20 | }
21 |
22 | #[cfg_attr(feature = "ipc", derive(Serialize, Deserialize))]
23 | pub struct VRFutureFrameData(State>);
24 |
25 | #[cfg_attr(feature = "ipc", derive(Serialize, Deserialize))]
26 | pub struct VRResolveFrameData(State<(), Sender>);
27 |
28 | impl VRFutureFrameData {
29 | pub fn resolved(data: VRFrameData) -> VRFutureFrameData {
30 | VRFutureFrameData(State::Resolved(data))
31 | }
32 |
33 | pub fn blocked() -> (VRResolveFrameData, VRFutureFrameData) {
34 | let (send, recv) = channel();
35 | (
36 | VRResolveFrameData(State::Blocked(send)),
37 | VRFutureFrameData(State::Blocked(recv)),
38 | )
39 | }
40 |
41 | pub fn block(self) -> VRFrameData {
42 | match self {
43 | VRFutureFrameData(State::Resolved(result)) => result,
44 | VRFutureFrameData(State::Blocked(recv)) => recv.recv().expect("Failed to get frame data"),
45 | }
46 | }
47 | }
48 |
49 | impl VRResolveFrameData {
50 | pub fn resolve(&mut self, data: VRFrameData) -> Result<(), ()> {
51 | match *self {
52 | VRResolveFrameData(State::Resolved(())) => return Err(()),
53 | VRResolveFrameData(State::Blocked(ref send)) => send.send(data).expect("Failed to put frame data"),
54 | };
55 | self.0 = State::Resolved(());
56 | Ok(())
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/rust-webvr-api/src/vr_gamepad.rs:
--------------------------------------------------------------------------------
1 | use std::sync::Arc;
2 | use std::cell::RefCell;
3 | use VRPose;
4 |
5 | pub type VRGamepadPtr = Arc>;
6 |
7 | pub trait VRGamepad {
8 | fn id(&self) -> u32;
9 | fn data(&self) -> VRGamepadData;
10 | fn state(&self) -> VRGamepadState;
11 | }
12 |
13 | #[derive(Debug, Clone)]
14 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
15 | pub struct VRGamepadState {
16 | pub gamepad_id: u32,
17 | pub connected: bool,
18 | pub timestamp: f64,
19 | pub axes: Vec,
20 | pub buttons: Vec,
21 | pub pose: VRPose
22 | }
23 |
24 | impl Default for VRGamepadState {
25 | fn default() -> VRGamepadState {
26 | VRGamepadState {
27 | gamepad_id: 0,
28 | connected: false,
29 | timestamp: 0.0,
30 | axes: Vec::new(),
31 | buttons: Vec::new(),
32 | pose: VRPose::default()
33 | }
34 | }
35 | }
36 |
37 | #[derive(Debug, Clone)]
38 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
39 | pub enum VRGamepadHand {
40 | Unknown,
41 | Left,
42 | Right
43 | }
44 |
45 | #[derive(Debug, Clone)]
46 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
47 | pub struct VRGamepadData {
48 | pub display_id: u32,
49 | pub name: String,
50 | pub hand: VRGamepadHand
51 | }
52 |
53 | impl Default for VRGamepadData {
54 | fn default() -> VRGamepadData {
55 | Self {
56 | display_id: 0,
57 | name: String::new(),
58 | hand: VRGamepadHand::Unknown
59 | }
60 | }
61 | }
62 |
63 | #[derive(Debug, Clone)]
64 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
65 | pub struct VRGamepadButton {
66 | pub pressed: bool,
67 | pub touched: bool
68 | }
69 |
70 | impl VRGamepadButton {
71 | pub fn new(pressed: bool) -> Self {
72 | Self {
73 | pressed: pressed,
74 | touched: pressed,
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/rust-webvr-api/src/lib.rs:
--------------------------------------------------------------------------------
1 | #[macro_use]
2 | macro_rules! identity_matrix {
3 | () => ([1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]);
4 | }
5 |
6 | #[cfg(all(feature = "jni_utils", target_os = "android"))]
7 | pub extern crate android_injected_glue;
8 |
9 | #[cfg(feature = "utils")]
10 | extern crate time;
11 |
12 | #[cfg(all(feature = "jni_utils", target_os = "android"))]
13 | pub mod jni_utils;
14 | #[cfg(feature = "utils")]
15 | pub mod utils;
16 |
17 | #[cfg(feature = "serde-serialization")]
18 | #[macro_use]
19 | extern crate serde_derive;
20 |
21 | #[cfg(feature = "ipc")]
22 | extern crate ipc_channel;
23 |
24 | extern crate sparkle;
25 |
26 |
27 | pub mod mock;
28 | pub mod vr_display;
29 | pub mod vr_service;
30 | pub mod vr_display_data;
31 | pub mod vr_display_capabilities;
32 | pub mod vr_eye;
33 | pub mod vr_eye_parameters;
34 | pub mod vr_framebuffer;
35 | pub mod vr_frame_data;
36 | pub mod vr_future_frame_data;
37 | pub mod vr_layer;
38 | pub mod vr_pose;
39 | pub mod vr_stage_parameters;
40 | pub mod vr_event;
41 | pub mod vr_field_view;
42 | pub mod vr_gamepad;
43 | pub mod vr_main_thread_heartbeat;
44 |
45 | pub use mock::{MockVRControlMsg, MockVRInit, MockVRView};
46 | pub use vr_display::{VRDisplay,VRDisplayPtr};
47 | pub use vr_service::{VRService,VRServiceCreator};
48 | pub use vr_display_data::VRDisplayData;
49 | pub use vr_display_capabilities::VRDisplayCapabilities;
50 | pub use vr_eye::VREye;
51 | pub use vr_eye_parameters::VREyeParameters;
52 | pub use vr_framebuffer::{VRFramebuffer, VRFramebufferAttributes, VRViewport};
53 | pub use vr_frame_data::VRFrameData;
54 | pub use vr_future_frame_data::VRFutureFrameData;
55 | pub use vr_future_frame_data::VRResolveFrameData;
56 | pub use vr_layer::VRLayer;
57 | pub use vr_pose::VRPose;
58 | pub use vr_stage_parameters::VRStageParameters;
59 | pub use vr_event::{VREvent, VRDisplayEvent, VRDisplayEventReason, VRGamepadEvent};
60 | pub use vr_field_view::VRFieldOfView;
61 | pub use vr_gamepad::{VRGamepad, VRGamepadPtr, VRGamepadHand,
62 | VRGamepadData, VRGamepadState, VRGamepadButton};
63 | pub use vr_main_thread_heartbeat::VRMainThreadHeartbeat;
64 |
--------------------------------------------------------------------------------
/rust-webvr-api/src/vr_frame_data.rs:
--------------------------------------------------------------------------------
1 | use VRPose;
2 | use std::mem;
3 | use std::ptr;
4 |
5 | /// Represents all the information needed to render a single frame of a VR scene
6 | #[derive(Debug, Clone)]
7 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
8 | pub struct VRFrameData {
9 | /// Monotonically increasing value that allows the author
10 | /// to determine if position state data been updated from the hardware
11 | pub timestamp: f64,
12 |
13 | /// major order column matrix describing the projection to be used for the left eye’s rendering
14 | pub left_projection_matrix: [f32; 16],
15 |
16 | /// major order column matrix describing the view transform to be used for the left eye’s rendering
17 | pub left_view_matrix: [f32; 16],
18 |
19 | /// major order column matrix describing the projection to be used for the right eye’s rendering
20 | pub right_projection_matrix: [f32; 16],
21 |
22 | /// major order column matrix describing the view transform to be used for the right eye’s rendering
23 | pub right_view_matrix: [f32; 16],
24 |
25 | /// VRPose containing the future predicted pose of the VRDisplay
26 | /// when the current frame will be presented.
27 | pub pose: VRPose,
28 | }
29 |
30 | impl Default for VRFrameData {
31 | fn default() -> VRFrameData {
32 | VRFrameData {
33 | timestamp: 0f64,
34 | left_projection_matrix: identity_matrix!(),
35 | left_view_matrix: identity_matrix!(),
36 | right_projection_matrix: identity_matrix!(),
37 | right_view_matrix: identity_matrix!(),
38 | pose: VRPose::default(),
39 | }
40 | }
41 | }
42 |
43 | impl VRFrameData {
44 | pub fn to_bytes(&self) -> Vec {
45 | let mut vec = vec![0u8; mem::size_of::()];
46 | unsafe {
47 | ptr::copy_nonoverlapping(self,
48 | vec.as_mut_ptr() as *mut VRFrameData,
49 | mem::size_of::());
50 | }
51 | vec
52 | }
53 |
54 | pub fn from_bytes(bytes: &[u8]) -> VRFrameData {
55 | unsafe {
56 | let mut result = mem::MaybeUninit::uninit();
57 | ptr::copy_nonoverlapping(bytes.as_ptr(),
58 | result.as_mut_ptr() as *mut u8,
59 | mem::size_of::());
60 | result.assume_init()
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/vrexternal/android/mozgfx.rs:
--------------------------------------------------------------------------------
1 | #![allow(non_upper_case_globals)]
2 | #![allow(non_camel_case_types)]
3 | #![allow(non_snake_case)]
4 | #![allow(dead_code)]
5 |
6 | use libc;
7 |
8 | include!(concat!(env!("OUT_DIR"), "/moz_external_vr.rs"));
9 |
10 | pub type PthreadResult = Result<(), i32>;
11 |
12 | impl VRExternalShmem {
13 | pub fn pull_system(&mut self, exit_cond: &Fn(&VRSystemState) -> bool) -> VRSystemState {
14 | self.systemMutex.lock().expect("systemMutex lock error");
15 | loop {
16 | if exit_cond(&self.state) {
17 | break;
18 | }
19 | self.systemCond.wait(&mut self.systemMutex).expect("systemCond wait error");
20 | }
21 | let state = self.state.clone();
22 | self.systemMutex.unlock().expect("systemMutex unlock error");
23 | state
24 | }
25 | pub fn pull_browser(&mut self) -> VRBrowserState {
26 | self.servoMutex.lock().expect("servoMutex lock error");
27 | let state = self.servoState.clone();
28 | self.servoMutex.unlock().expect("servoMutex unlock error");
29 | state
30 | }
31 | pub fn push_browser(&mut self, state: VRBrowserState) {
32 | self.servoMutex.lock().expect("servoMutex lock error");
33 | self.servoState = state;
34 | self.servoCond.signal().expect("servoCond signal error");
35 | self.servoMutex.unlock().expect("servoMutex unlock error");
36 | }
37 | }
38 |
39 | impl pthread_mutex_t {
40 | fn as_libc(&mut self) -> *mut libc::pthread_mutex_t {
41 | self as *mut _ as *mut libc::pthread_mutex_t
42 | }
43 | pub fn lock(&mut self) -> PthreadResult {
44 | let r = unsafe { libc::pthread_mutex_lock(self.as_libc()) };
45 | if r == 0 { Ok(()) } else { Err(r) }
46 | }
47 | pub fn unlock(&mut self) -> PthreadResult {
48 | let r = unsafe { libc::pthread_mutex_unlock(self.as_libc()) };
49 | if r == 0 { Ok(()) } else { Err(r) }
50 | }
51 | }
52 |
53 | impl pthread_cond_t {
54 | fn as_libc(&mut self) -> *mut libc::pthread_cond_t {
55 | self as *mut _ as *mut libc::pthread_cond_t
56 | }
57 | pub fn wait(&mut self, mutex: &mut pthread_mutex_t) -> PthreadResult {
58 | let r = unsafe { libc::pthread_cond_wait(self.as_libc(), mutex.as_libc()) };
59 | if r == 0 { Ok(()) } else { Err(r) }
60 | }
61 | pub fn signal(&mut self) -> PthreadResult {
62 | let r = unsafe { libc::pthread_cond_signal(self.as_libc()) };
63 | if r == 0 { Ok(()) } else { Err(r) }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/examples/room/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/googlevr/gradle/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/oculusvr/gradle/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/glwindow/service.rs:
--------------------------------------------------------------------------------
1 | use gleam::gl::Gl;
2 | use glutin::{WindowedContext, NotCurrent};
3 | use glutin::EventsLoop;
4 | use glutin::EventsLoopClosed;
5 | use glutin::dpi::PhysicalSize;
6 | use rust_webvr_api::VRDisplayPtr;
7 | use rust_webvr_api::VREvent;
8 | use rust_webvr_api::VRGamepadPtr;
9 | use rust_webvr_api::VRService;
10 | use std::cell::RefCell;
11 | use std::rc::Rc;
12 | use std::sync::Arc;
13 | use std::sync::mpsc::channel;
14 | use std::sync::mpsc::Sender;
15 | use super::display::GlWindowVRDisplay;
16 | use super::display::GlWindowVRDisplayPtr;
17 | use super::heartbeat::GlWindowVRMainThreadHeartbeat;
18 | use super::heartbeat::GlWindowVRMessage;
19 |
20 | pub struct GlWindowVRService {
21 | name: String,
22 | size: PhysicalSize,
23 | sender: Sender,
24 | display: Option,
25 | }
26 |
27 | // This is very very unsafe, but the API requires it.
28 | unsafe impl Send for GlWindowVRService {}
29 |
30 | impl VRService for GlWindowVRService {
31 | fn initialize(&mut self) -> Result<(), String> {
32 | self.get_display();
33 | Ok(())
34 | }
35 |
36 | fn fetch_displays(&mut self) -> Result, String> {
37 | Ok(vec![ self.get_display().clone() ])
38 | }
39 |
40 | fn fetch_gamepads(&mut self) -> Result, String> {
41 | Ok(vec![])
42 | }
43 |
44 | fn is_available(&self) -> bool {
45 | true
46 | }
47 |
48 | fn poll_events(&self) -> Vec {
49 | vec![]
50 | }
51 | }
52 |
53 | impl GlWindowVRService {
54 | // This function should be called from the main thread.
55 | pub fn new(
56 | name: String,
57 | gl_context: WindowedContext,
58 | events_loop_factory: EventsLoopFactory,
59 | gl: Rc,
60 | ) -> (GlWindowVRService, GlWindowVRMainThreadHeartbeat) {
61 | let (sender, receiver) = channel();
62 | let size = gl_context.window().get_inner_size().expect("No window size");
63 | let hidpi = gl_context.window().get_hidpi_factor();
64 | let heartbeat = GlWindowVRMainThreadHeartbeat::new(receiver, gl_context, events_loop_factory, gl);
65 | let service = GlWindowVRService {
66 | name: name,
67 | size: size.to_physical(hidpi),
68 | sender: sender,
69 | display: None,
70 | };
71 | (service, heartbeat)
72 | }
73 |
74 | fn get_display(&mut self) -> &mut GlWindowVRDisplayPtr {
75 | let name = &self.name;
76 | let sender = &self.sender;
77 | let size = self.size;
78 | self.display.get_or_insert_with(|| {
79 | let display = GlWindowVRDisplay::new(name.clone(), size, sender.clone());
80 | Arc::new(RefCell::new(display))
81 | })
82 | }
83 | }
84 |
85 | pub type EventsLoopFactory = Box Result>;
86 |
--------------------------------------------------------------------------------
/rust-webvr-api/src/vr_event.rs:
--------------------------------------------------------------------------------
1 | use {VRDisplayData, VRGamepadData, VRGamepadState};
2 |
3 | #[derive(Debug, Clone)]
4 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
5 | pub enum VREvent {
6 | Display(VRDisplayEvent),
7 | Gamepad(VRGamepadEvent),
8 | }
9 |
10 | #[derive(Debug, Clone, Copy)]
11 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
12 | pub enum VRDisplayEventReason {
13 | Navigation,
14 | /// The VRDisplay has detected that the user has put it on.
15 | Mounted,
16 |
17 | /// The VRDisplay has detected that the user has taken it off.
18 | Unmounted
19 | }
20 |
21 | #[derive(Debug, Clone)]
22 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
23 | pub enum VRDisplayEvent {
24 | /// Indicates that a VRDisplay has been connected.
25 | Connect(VRDisplayData),
26 |
27 | /// Indicates that a VRDisplay has been disconnected.
28 | /// param: display_id
29 | Disconnect(u32),
30 |
31 | /// Indicates that something has occured which suggests the VRDisplay should be presented to
32 | Activate(VRDisplayData, VRDisplayEventReason),
33 |
34 | /// Indicates that something has occured which suggests the VRDisplay should exit presentation
35 | Deactivate(VRDisplayData, VRDisplayEventReason),
36 |
37 | /// Indicates that some of the VRDisplay's data has changed (eye parameters, tracking data, chaperone, ipd, etc.)
38 | Change(VRDisplayData),
39 |
40 | /// Indicates that presentation to the display by the page is paused by the user agent, OS, or VR hardware
41 | Blur(VRDisplayData),
42 |
43 | /// Indicates that presentation to the display by the page has resumed after being blurred.
44 | Focus(VRDisplayData),
45 |
46 | /// Indicates that a VRDisplay has begun or ended VR presentation
47 | PresentChange(VRDisplayData, bool),
48 |
49 | /// Indicates that VRDisplay presentation loop must be paused (i.e Android app goes to background)
50 | Pause(u32),
51 |
52 | /// Indicates that VRDisplay presentation loop must be resumed (i.e Android app goes to foreground)
53 | Resume(u32),
54 |
55 | /// Indicates that user has exited VRDisplay presentation (i.e. User clicked back key on android)
56 | Exit(u32)
57 | }
58 |
59 | impl Into for VRDisplayEvent {
60 | fn into(self) -> VREvent {
61 | VREvent::Display(self)
62 | }
63 | }
64 |
65 |
66 | #[derive(Debug, Clone)]
67 | #[cfg_attr(feature = "serde-serialization", derive(Deserialize, Serialize))]
68 | pub enum VRGamepadEvent {
69 | /// Indicates that a VRGamepad has been connected.
70 | /// params: name, displa_id, state
71 | Connect(VRGamepadData, VRGamepadState),
72 |
73 | /// Indicates that a VRGamepad has been disconnected.
74 | /// param: gamepad_id
75 | Disconnect(u32)
76 | }
77 |
78 | impl Into for VRGamepadEvent {
79 | fn into(self) -> VREvent {
80 | VREvent::Gamepad(self)
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/magicleap/service.rs:
--------------------------------------------------------------------------------
1 | use egl::types::EGLContext;
2 | use gleam::gl::Gl;
3 | use rust_webvr_api::VRDisplayEvent;
4 | use rust_webvr_api::VRDisplayEventReason;
5 | use rust_webvr_api::VRDisplayPtr;
6 | use rust_webvr_api::VREvent;
7 | use rust_webvr_api::VRGamepadPtr;
8 | use rust_webvr_api::VRService;
9 | use std::cell::RefCell;
10 | use std::mem;
11 | use std::rc::Rc;
12 | use std::sync::Arc;
13 | use std::sync::mpsc::channel;
14 | use std::sync::mpsc::Sender;
15 | use super::display::MagicLeapVRDisplay;
16 | use super::display::MagicLeapVRDisplayPtr;
17 | use super::heartbeat::MagicLeapVRMainThreadHeartbeat;
18 | use super::heartbeat::MagicLeapVRMessage;
19 | use super::magicleap_c_api::MLResult;
20 |
21 | pub struct MagicLeapVRService {
22 | sender: Sender,
23 | display: Option>,
24 | events: RefCell>,
25 | }
26 |
27 | // This is very very unsafe, but the API requires it.
28 | unsafe impl Send for MagicLeapVRService {}
29 |
30 | impl VRService for MagicLeapVRService {
31 | fn initialize(&mut self) -> Result<(), String> {
32 | self.get_display()?;
33 | Ok(())
34 | }
35 |
36 | fn fetch_displays(&mut self) -> Result, String> {
37 | Ok(vec![ self.get_display()? ])
38 | }
39 |
40 | fn fetch_gamepads(&mut self) -> Result, String> {
41 | Ok(vec![])
42 | }
43 |
44 | fn is_available(&self) -> bool {
45 | true
46 | }
47 |
48 | fn poll_events(&self) -> Vec {
49 | mem::replace(&mut *self.events.borrow_mut(), Vec::new())
50 | }
51 | }
52 |
53 | impl MagicLeapVRService {
54 | // This function should be called from the main thread.
55 | pub fn new(
56 | display_name: String,
57 | egl: EGLContext,
58 | gl: Rc,
59 | ) -> Result<(MagicLeapVRService, MagicLeapVRMainThreadHeartbeat), MLResult> {
60 | info!("Creating VRService");
61 | let (sender, receiver) = channel();
62 | let heartbeat = MagicLeapVRMainThreadHeartbeat::new(display_name, receiver, egl, gl)?;
63 | let service = MagicLeapVRService {
64 | sender: sender,
65 | display: None,
66 | events: RefCell::new(Vec::new()),
67 | };
68 | Ok((service, heartbeat))
69 | }
70 |
71 | fn get_display(&mut self) -> Result {
72 | let sender = &self.sender;
73 | let events = &self.events;
74 | self.display.get_or_insert_with(|| {
75 | let (dsender, dreceiver) = channel();
76 | let _ = sender.send(MagicLeapVRMessage::GetDisplayData(dsender));
77 | let display_data = dreceiver.recv().unwrap_or(Err(MLResult::UnspecifiedFailure))?;
78 | let display = MagicLeapVRDisplay::new(display_data.clone(), sender.clone());
79 | let event = VREvent::Display(VRDisplayEvent::Activate(display_data, VRDisplayEventReason::Mounted));
80 | events.borrow_mut().push(event);
81 | Ok(Arc::new(RefCell::new(display)))
82 | }).clone()
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/examples/room/build.rs:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | use std::env;
6 | use std::path::Path;
7 | use std::process;
8 | use std::process::{Command, Stdio};
9 |
10 | fn main() {
11 | // build.rs is not platform-specific, so we have to check the target here.
12 | let target = env::var("TARGET").unwrap();
13 | if target.contains("android") {
14 | android_main()
15 | }
16 | }
17 |
18 | fn android_main() {
19 | // Get the NDK path from NDK_HOME env.
20 | let ndk_path = env::var("ANDROID_NDK").ok().expect("Please set the ANDROID_NDK environment variable");
21 | let ndk_path = Path::new(&ndk_path);
22 |
23 | // Build up the path to the NDK compilers
24 | // Options for host are: "linux-x86_64" "linux-x86" "darwin-x86_64" "darwin-x86"
25 | // per: https://android.googlesource.com/platform/ndk/+/ics-mr0/docs/STANDALONE-TOOLCHAIN.html
26 |
27 | let host = env::var("HOST").unwrap();
28 | let google_host = match host.as_ref() {
29 | "i686-unknown-linux-gnu" => "linux-x86",
30 | "x86_64-apple-darwin" => "darwin-x86_64",
31 | "x86_64-unknown-linux-gnu" => "linux-x86_64",
32 | _ => panic!("Unknown support android cross-compile host: {}", host)
33 | };
34 |
35 | let toolchain_path = ndk_path.join("toolchains").join("arm-linux-androideabi-4.9").join("prebuilt").
36 | join(google_host);
37 | println!("toolchain path is: {}", toolchain_path.to_str().unwrap());
38 |
39 | let target = env::var("TARGET").unwrap();
40 | let arch = if target.contains("arm") {
41 | "arch-arm"
42 | } else if target.contains("x86") {
43 | "arch-x86"
44 | } else if target.contains("mips") {
45 | "arch-mips"
46 | } else {
47 | panic!("Invalid target architecture {}", target);
48 | };
49 |
50 | // Get the output directory.
51 | let out_dir = env::var("OUT_DIR").ok().expect("Cargo should have set the OUT_DIR environment variable");
52 | let directory = Path::new(&out_dir);
53 |
54 | // compiling android_native_app_glue.c
55 | if Command::new(toolchain_path.join("bin").join("arm-linux-androideabi-gcc"))
56 | .arg(ndk_path.join("sources").join("android").join("native_app_glue").join("android_native_app_glue.c"))
57 | .arg("-c")
58 | .arg("-o").arg(directory.join("android_native_app_glue.o"))
59 | .arg("--sysroot").arg(ndk_path.join("platforms").join("android-18").join(arch))
60 | .stdout(Stdio::inherit())
61 | .stderr(Stdio::inherit())
62 | .status().unwrap().code().unwrap() != 0
63 | {
64 | println!("Error while executing gcc");
65 | process::exit(1)
66 | }
67 |
68 | // compiling libandroid_native_app_glue.a
69 | if Command::new(toolchain_path.join("bin").join("arm-linux-androideabi-ar"))
70 | .arg("rcs")
71 | .arg(directory.join("libandroid_native_app_glue.a"))
72 | .arg(directory.join("android_native_app_glue.o"))
73 | .stdout(Stdio::inherit())
74 | .stderr(Stdio::inherit())
75 | .status().unwrap().code().unwrap() != 0
76 | {
77 | println!("Error while executing ar");
78 | process::exit(1)
79 | }
80 |
81 | println!("cargo:rustc-link-lib=static=android_native_app_glue");
82 | println!("cargo:rustc-link-search=native={}", out_dir);
83 | println!("cargo:rustc-link-lib=log");
84 | println!("cargo:rustc-link-lib=android");
85 | }
86 |
--------------------------------------------------------------------------------
/rust-webvr-api/src/vr_display.rs:
--------------------------------------------------------------------------------
1 | use {VRDisplayData, VRFramebuffer, VRFramebufferAttributes, VRFrameData, VRFutureFrameData, VRGamepadPtr, VRLayer};
2 | use sparkle::gl::Gl;
3 | use std::sync::Arc;
4 | use std::cell::RefCell;
5 | pub type VRDisplayPtr = Arc>;
6 |
7 | /// The VRDisplay traits forms the base of all VR device implementations
8 | pub trait VRDisplay: Send + Sync {
9 |
10 | /// Returns unique device identifier
11 | fn id(&self) -> u32;
12 |
13 | /// Returns the current display data.
14 | fn data(&self) -> VRDisplayData;
15 |
16 | /// Returns gamepads attached to this display
17 | fn fetch_gamepads(&mut self) -> Result, String>;
18 |
19 | /// Returns the immediate VRFrameData of the HMD
20 | /// Should be used when not presenting to the device.
21 | fn immediate_frame_data(&self, near_z: f64, far_z: f64) -> VRFrameData;
22 |
23 | /// Returns the synced VRFrameData to render the next frame.
24 | /// The future that is returned will resolve with frame data when the
25 | /// next frame is available.
26 | #[allow(deprecated)]
27 | fn future_frame_data(&mut self, near_z: f64, far_z: f64) -> VRFutureFrameData {
28 | // The default implementation blocks waiting for the display.
29 | self.sync_poses();
30 | VRFutureFrameData::resolved(self.synced_frame_data(near_z, far_z))
31 | }
32 |
33 | /// Returns the synced VRFrameData to render the current frame.
34 | /// Should be used when presenting to the device.
35 | /// sync_poses must have been called before this call.
36 | #[deprecated(since="0.10.3", note="please use `future_frame_data` instead")]
37 | fn synced_frame_data(&self, next: f64, far_z: f64) -> VRFrameData;
38 |
39 | /// Resets the pose for this display
40 | fn reset_pose(&mut self);
41 |
42 | /// Synchronization point to keep in step with the HMD
43 | /// Returns VRFrameData to be used in the next render frame
44 | /// Must be called in the render thread, before doing any work
45 | #[deprecated(since="0.10.3", note="please use `future_frame_data` instead")]
46 | fn sync_poses(&mut self);
47 |
48 | /// Binds the framebuffer to directly render to the HDM
49 | /// Must be called in the render thread, before doing any work
50 | fn bind_framebuffer(&mut self, index: u32);
51 |
52 | /// Returns the available FBOs that must be used to render to all eyes
53 | /// Must be called in the render thread, before doing any work
54 | fn get_framebuffers(&self) -> Vec;
55 |
56 | /// Renders a VRLayer from a external texture
57 | /// Must be called in the render thread
58 | #[deprecated(since="0.10.3", note="please use `submit_layer` instead")]
59 | fn render_layer(&mut self, layer: &VRLayer);
60 |
61 | /// Submits frame to the display
62 | /// Must be called in the render thread
63 | #[deprecated(since="0.10.3", note="please use `submit_layer` instead")]
64 | fn submit_frame(&mut self);
65 |
66 | /// Renders a VRLayer from an external texture, and submits it to the device.
67 | /// Must be called in the render thread
68 | #[allow(unused_variables)]
69 | #[allow(deprecated)]
70 | fn submit_layer(&mut self, gl: &Gl, layer: &VRLayer) {
71 | self.render_layer(layer);
72 | self.submit_frame();
73 | }
74 |
75 | /// Hint to indicate that we are going to start sending frames to the device
76 | fn start_present(&mut self, _attributes: Option) {}
77 |
78 | /// Hint to indicate that we are going to stop sending frames to the device
79 | fn stop_present(&mut self) {}
80 | }
81 |
82 | impl PartialEq for dyn VRDisplay {
83 | fn eq(&self, other: &dyn VRDisplay) -> bool {
84 | self.id() == other.id()
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/rust-webvr-api/src/jni_utils.rs:
--------------------------------------------------------------------------------
1 | #[cfg(all(feature = "jni_utils", target_os = "android"))]
2 |
3 | use android_injected_glue as android;
4 | use android_injected_glue::ffi as ndk;
5 | use std::ffi::CString;
6 | use std::ptr;
7 | use std::mem;
8 |
9 | pub struct JNIScope {
10 | pub vm: *mut ndk::_JavaVM,
11 | pub env: *mut ndk::JNIEnv,
12 | pub activity: ndk::jobject,
13 | }
14 |
15 | impl JNIScope {
16 | pub unsafe fn attach() -> Result {
17 | let mut env: *mut ndk::JNIEnv = mem::uninitialized();
18 | let activity: &ndk::ANativeActivity = mem::transmute(android::get_app().activity);
19 | let vm: &mut ndk::_JavaVM = mem::transmute(activity.vm);
20 | let vmf: &ndk::JNIInvokeInterface = mem::transmute(vm.functions);
21 |
22 | // Attach is required because native_glue is running in a separate thread
23 | if (vmf.AttachCurrentThread)(vm as *mut _, &mut env as *mut _, ptr::null_mut()) != 0 {
24 | return Err("JNI AttachCurrentThread failed".into());
25 | }
26 |
27 | Ok(JNIScope {
28 | vm: vm,
29 | env: env,
30 | activity: activity.clazz
31 | })
32 | }
33 |
34 | pub unsafe fn find_class(&self, class_name: &str) -> Result {
35 | // jni.FindClass doesn't find our classes because the attached thread has not our classloader.
36 | // NativeActivity's classloader is used to fix this issue.
37 | let env = self.env;
38 | let jni = self.jni();
39 |
40 | let activity_class = (jni.GetObjectClass)(env, self.activity);
41 | if activity_class.is_null() {
42 | return Err("Didn't find NativeActivity class".into());
43 | }
44 | let method = self.get_method(activity_class, "getClassLoader", "()Ljava/lang/ClassLoader;", false);
45 | let classloader = (jni.CallObjectMethod)(env, self.activity, method);
46 | if classloader.is_null() {
47 | return Err("Didn't find NativeActivity's classloader".into());
48 | }
49 | let classloader_class = (jni.GetObjectClass)(env, classloader);
50 | let load_method = self.get_method(classloader_class, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;", false);
51 |
52 | // Load our class using the classloader.
53 | let class_name = CString::new(class_name).unwrap();
54 | let class_name = (jni.NewStringUTF)(env, class_name.as_ptr());
55 | let java_class = (jni.CallObjectMethod)(env, classloader, load_method, class_name) as ndk::jclass;
56 | (jni.DeleteLocalRef)(env, class_name);
57 |
58 | Ok(java_class)
59 | }
60 |
61 | pub unsafe fn get_method(&self,
62 | class: ndk::jclass,
63 | method: &str,
64 | signature: &str,
65 | is_static: bool) -> ndk::jmethodID {
66 | let method = CString::new(method).unwrap();
67 | let signature = CString::new(signature).unwrap();
68 | let jni = self.jni();
69 |
70 | if is_static {
71 | (jni.GetStaticMethodID)(self.env, class, method.as_ptr(), signature.as_ptr())
72 | } else {
73 | (jni.GetMethodID)(self.env, class, method.as_ptr(), signature.as_ptr())
74 | }
75 | }
76 |
77 | pub fn jni(&self) -> &mut ndk::JNINativeInterface {
78 | unsafe {
79 | mem::transmute((*self.env).functions)
80 | }
81 | }
82 |
83 | pub fn jni_from_env(env: *mut ndk::JNIEnv) -> *mut ndk::JNINativeInterface {
84 | unsafe {
85 | (*env).functions as *mut _
86 | }
87 | }
88 | }
89 |
90 | impl Drop for JNIScope {
91 | // Autodetach JNI thread
92 | fn drop(&mut self) {
93 | unsafe {
94 | let vmf: &ndk::JNIInvokeInterface = mem::transmute((*self.vm).functions);
95 | (vmf.DetachCurrentThread)(self.vm);
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/openvr/gamepad.rs:
--------------------------------------------------------------------------------
1 | use super::binding as openvr;
2 | use super::binding::ETrackingUniverseOrigin::*;
3 | use super::binding::EVRButtonId;
4 | use super::binding::EVRButtonId::*;
5 | use super::display::OpenVRDisplay;
6 | use rust_webvr_api::utils;
7 | use std::cell::RefCell;
8 | use std::mem;
9 | use std::sync::Arc;
10 | use {VRGamepad, VRGamepadData, VRGamepadHand, VRGamepadState, VRGamepadButton};
11 |
12 | pub type OpenVRGamepadPtr = Arc>;
13 |
14 | pub struct OpenVRGamepad {
15 | gamepad_id: u32,
16 | display_id: u32,
17 | index: openvr::TrackedDeviceIndex_t,
18 | system: *mut openvr::VR_IVRSystem_FnTable
19 | }
20 |
21 | unsafe impl Send for OpenVRGamepad {}
22 | unsafe impl Sync for OpenVRGamepad {}
23 |
24 | impl OpenVRGamepad {
25 | pub fn new(index: openvr::TrackedDeviceIndex_t,
26 | system: *mut openvr::VR_IVRSystem_FnTable,
27 | display_id: u32)
28 | -> Arc> {
29 | Arc::new(RefCell::new(OpenVRGamepad {
30 | gamepad_id: utils::new_id(),
31 | display_id: display_id,
32 | index: index,
33 | system: system
34 | }))
35 | }
36 |
37 | pub fn index(&self) -> openvr::TrackedDeviceIndex_t {
38 | self.index
39 | }
40 | }
41 |
42 | impl VRGamepad for OpenVRGamepad {
43 | fn id(&self) -> u32 {
44 | self.gamepad_id
45 | }
46 |
47 | fn data(&self) -> VRGamepadData {
48 | VRGamepadData {
49 | display_id: self.display_id,
50 | name: format!("OpenVR {:?}", self.index),
51 | hand: VRGamepadHand::Unknown
52 | }
53 | }
54 |
55 | fn state(&self) -> VRGamepadState {
56 | let mut state = VRGamepadState::default();
57 |
58 | state.gamepad_id = self.gamepad_id;
59 | let mut controller: openvr::VRControllerState_t = unsafe { mem::uninitialized() };
60 | let mut tracked_poses: [openvr::TrackedDevicePose_t; openvr::k_unMaxTrackedDeviceCount as usize]
61 | = unsafe { mem::uninitialized() };
62 |
63 | unsafe {
64 | (*self.system).GetControllerState.unwrap()(self.index,
65 | &mut controller,
66 | mem::size_of::() as u32);
67 | (*self.system).GetDeviceToAbsoluteTrackingPose.unwrap()(ETrackingUniverseOrigin_TrackingUniverseSeated,
68 | 0.04f32,
69 | &mut tracked_poses[0],
70 | openvr::k_unMaxTrackedDeviceCount);
71 | }
72 | let pose = &tracked_poses[self.index as usize];
73 |
74 | state.connected = pose.bDeviceIsConnected;
75 |
76 | let trackpad = controller.rAxis[0];
77 | // Analog trigger data is in only the X axis
78 | let trigger = controller.rAxis[1];
79 | state.axes = [trackpad.x as f64, trackpad.y as f64, trigger.x as f64].to_vec();
80 |
81 | // TODO: check spec order
82 | let buttons = [
83 | button_mask(EVRButtonId_k_EButton_Grip),
84 | button_mask(EVRButtonId_k_EButton_ApplicationMenu)
85 | ];
86 |
87 | for mask in buttons.iter() {
88 | state.buttons.push(VRGamepadButton {
89 | pressed: (controller.ulButtonPressed & mask) != 0,
90 | touched: (controller.ulButtonTouched & mask) != 0
91 | });
92 | }
93 |
94 | OpenVRDisplay::fetch_pose(&pose, &mut state.pose);
95 |
96 | state
97 | }
98 | }
99 |
100 | #[inline]
101 | fn button_mask(id: EVRButtonId) -> u64 {
102 | 1u64 << (id as u32)
103 | }
104 |
--------------------------------------------------------------------------------
/rust-webvr/build.rs:
--------------------------------------------------------------------------------
1 | extern crate bindgen;
2 | extern crate gl_generator;
3 |
4 | use std::env;
5 | use std::fs::{self, File};
6 | use std::path::{Path, PathBuf};
7 | use gl_generator::{Registry, Api, Profile, Fallbacks};
8 |
9 | fn main() {
10 | // Copy AARs
11 | if let Ok(aar_out_dir) = env::var("AAR_OUT_DIR") {
12 | fs::copy(&Path::new("src/api/googlevr/aar/GVRService.aar"),
13 | &Path::new(&aar_out_dir).join("GVRService.aar")).unwrap();
14 |
15 | fs::copy(&Path::new("src/api/oculusvr/aar/OVRService.aar"),
16 | &Path::new(&aar_out_dir).join("OVRService.aar")).unwrap();
17 | }
18 |
19 | if !cfg!(feature = "googlevr") && !cfg!(feature = "oculusvr") && !cfg!(feature = "vrexternal") && !cfg!(feature = "magicleap") {
20 | return;
21 | }
22 |
23 | let out_dir = env::var("OUT_DIR").unwrap();
24 |
25 | // GLES 2.0 bindings
26 | let mut file = File::create(&Path::new(&out_dir).join("gles_bindings.rs")).unwrap();
27 | let gles_reg = Registry::new(Api::Gles2, (3, 0), Profile::Core, Fallbacks::All, [
28 | "GL_OVR_multiview2", "GL_OVR_multiview", "GL_OVR_multiview_multisampled_render_to_texture"]);
29 | gles_reg.write_bindings(gl_generator::StaticGenerator, &mut file)
30 | .unwrap();
31 |
32 | // EGL bindings
33 | if cfg!(any(feature = "magicleap", feature = "oculusvr")) {
34 | let mut file = File::create(&Path::new(&out_dir).join("egl_bindings.rs")).unwrap();
35 | Registry::new(Api::Egl, (1, 5), Profile::Core, Fallbacks::All, ["EGL_KHR_fence_sync"])
36 | .write_bindings(gl_generator::StaticGenerator, &mut file).unwrap();
37 | println!("cargo:rustc-link-lib=EGL");
38 | }
39 |
40 | // Magicleap C API
41 | if cfg!(feature = "magicleap") {
42 | let mut builder = bindgen::Builder::default()
43 | .header("src/api/magicleap/magicleap_c_api.h")
44 | .blacklist_type("MLResult")
45 | .size_t_is_usize(true)
46 | .derive_default(true)
47 | .rustfmt_bindings(true);
48 |
49 | if let Ok(mlsdk) = env::var("MAGICLEAP_SDK") {
50 | builder = builder.clang_args(&[
51 | format!("--no-standard-includes"),
52 | format!("--sysroot={}", mlsdk),
53 | format!("-I{}/include", mlsdk),
54 | format!("-I{}/lumin/usr/include", mlsdk),
55 | format!("-I{}/tools/toolchains/lib64/clang/3.8/include", mlsdk),
56 | ]);
57 | }
58 |
59 | if let Ok(flags) = env::var("CFLAGS") {
60 | for flag in flags.split_whitespace() {
61 | builder = builder.clang_arg(flag);
62 | }
63 | }
64 |
65 | if let Ok(flags) = env::var("CLANGFLAGS") {
66 | for flag in flags.split_whitespace() {
67 | builder = builder.clang_arg(flag);
68 | }
69 | }
70 |
71 | let bindings = builder.generate().expect("Unable to generate bindings");
72 | let out_path = PathBuf::from(&out_dir);
73 | bindings.write_to_file(out_path.join("magicleap_c_api.rs"))
74 | .expect("Couldn't write bindings!");
75 | }
76 |
77 | let target = env::var("TARGET").unwrap();
78 | if target.contains("android") && cfg!(feature = "vrexternal") {
79 | let mut builder = bindgen::Builder::default()
80 | .header("src/api/vrexternal/cpp/moz_external_vr.h")
81 | .clang_args(&["-x", "c++", "-std=gnu++11"])
82 | .whitelist_type("mozilla::gfx::VRExternalShmem")
83 | .disable_name_namespacing()
84 | .derive_default(true)
85 | .rustfmt_bindings(true);
86 |
87 | if let Ok(flags) = env::var("CXXFLAGS") {
88 | for flag in flags.split_whitespace() {
89 | builder = builder.clang_arg(flag);
90 | }
91 | }
92 |
93 | if let Ok(flags) = env::var("CLANGFLAGS") {
94 | for flag in flags.split_whitespace() {
95 | builder = builder.clang_arg(flag);
96 | }
97 | }
98 |
99 | let bindings = builder.generate().expect("Unable to generate bindings");
100 | let out_path = PathBuf::from(&out_dir);
101 | bindings.write_to_file(out_path.join("moz_external_vr.rs"))
102 | .expect("Couldn't write bindings!");
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/examples/room/android/app/src/main/java/com/rust/webvr/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.rust.webvr;
2 | import android.graphics.Color;
3 | import android.os.Bundle;
4 | import android.view.SurfaceHolder;
5 | import android.view.SurfaceView;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.view.WindowManager;
9 | import android.widget.FrameLayout;
10 |
11 | import java.io.BufferedInputStream;
12 | import java.io.File;
13 | import java.io.FileOutputStream;
14 | import java.io.IOException;
15 | import java.lang.System;
16 | import java.util.Enumeration;
17 | import java.util.zip.ZipEntry;
18 | import java.util.zip.ZipFile;
19 |
20 | public class MainActivity extends android.app.NativeActivity {
21 | private static final String LOGTAG = "WebVRExample";
22 |
23 | static {
24 | //System.loadLibrary("gvr");
25 | //System.loadLibrary("gvr_audio");
26 | System.loadLibrary("vrapi");
27 | System.loadLibrary("webvr");
28 | }
29 |
30 | @Override
31 | public void onCreate(Bundle savedInstanceState) {
32 | try {
33 | extractAssets();
34 | } catch (IOException e) {
35 | throw new RuntimeException(e);
36 | }
37 |
38 | super.onCreate(savedInstanceState);
39 | getWindow().takeSurface(null);
40 |
41 | FrameLayout layout = new FrameLayout(this);
42 | layout.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
43 | FrameLayout.LayoutParams.MATCH_PARENT));
44 | SurfaceView nativeSurface = new SurfaceView(this);
45 | nativeSurface.getHolder().addCallback(this);
46 | layout.addView(nativeSurface, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
47 | setContentView(layout);
48 |
49 | keepScreenOn();
50 | addFullScreenListener();
51 | }
52 |
53 | @Override
54 | protected void onResume() {
55 | setFullScreen();
56 | super.onResume();
57 | }
58 |
59 | // keep the device's screen turned on and bright.
60 | private void keepScreenOn() {
61 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
62 | }
63 |
64 | // Dim toolbar and make the view fullscreen
65 | private void setFullScreen() {
66 | View decorView = getWindow().getDecorView();
67 | decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
68 | | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
69 | | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
70 | | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
71 | | View.SYSTEM_UI_FLAG_FULLSCREEN
72 | | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
73 | }
74 |
75 | // This is here to make the toolbar autohide after 2 seconds of being touched
76 | private void addFullScreenListener() {
77 | View decorView = getWindow().getDecorView();
78 | decorView.setOnSystemUiVisibilityChangeListener(
79 | new View.OnSystemUiVisibilityChangeListener() {
80 | public void onSystemUiVisibilityChange(int visibility) {
81 | if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
82 | setFullScreen();
83 | }
84 | }
85 | });
86 | }
87 |
88 | private void extractAssets() throws IOException {
89 | String path = getExternalFilesDir(null) + "/res";
90 |
91 | ZipFile zipFile = null;
92 | File targetDir = new File(path);
93 | try {
94 | zipFile = new ZipFile(this.getApplicationInfo().sourceDir);
95 | for (Enumeration extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
96 | ZipEntry entry = e.nextElement();
97 | if (entry.isDirectory() || !entry.getName().startsWith("assets/")) {
98 | continue;
99 | }
100 | File targetFile = new File(targetDir, entry.getName().substring("assets/".length()));
101 | targetFile.getParentFile().mkdirs();
102 | byte[] tempBuffer = new byte[(int)entry.getSize()];
103 | BufferedInputStream is = null;
104 | FileOutputStream os = null;
105 | try {
106 | is = new BufferedInputStream(zipFile.getInputStream(entry));
107 | os = new FileOutputStream(targetFile);
108 | is.read(tempBuffer);
109 | os.write(tempBuffer);
110 | } finally {
111 | if (is != null) is.close();
112 | if (os != null) os.close();
113 | }
114 | }
115 | } finally {
116 | if (zipFile != null) zipFile.close();
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/examples/room/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.2"
6 |
7 | defaultConfig {
8 | applicationId "com.rust.webvr"
9 | minSdkVersion 21
10 | targetSdkVersion 25
11 | versionCode 1
12 | versionName "1.0.0"
13 | jackOptions {
14 | enabled true
15 | }
16 | }
17 |
18 | compileOptions {
19 | incremental false
20 | }
21 |
22 | splits {
23 | density {
24 | enable false
25 | }
26 | abi {
27 | enable false
28 | }
29 | }
30 |
31 | productFlavors {
32 | daydream {
33 | }
34 | gearvr {
35 | }
36 | }
37 |
38 | sourceSets {
39 | main {
40 | java.srcDirs = ['src/main/java']
41 | assets.srcDirs = ['../../res']
42 | }
43 | armDebug {
44 | jniLibs.srcDirs = getJniLibsPath(true, 'arm')
45 | }
46 | armRelease {
47 | jniLibs.srcDirs = getJniLibsPath(false, 'arm')
48 | }
49 | armv7Debug {
50 | jniLibs.srcDirs = getJniLibsPath(true, 'armv7')
51 | }
52 | armv7Release {
53 | jniLibs.srcDirs = getJniLibsPath(false, 'armv7')
54 | }
55 | }
56 |
57 | buildTypes {
58 | // Default debug and release build types are used as templates
59 | debug {
60 | jniDebuggable true
61 | }
62 |
63 | release {
64 | debuggable true
65 | signingConfig signingConfigs.debug // Change this to sign with a production key
66 | minifyEnabled false
67 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
68 | }
69 |
70 | // Custom build types
71 | armDebug {
72 | initWith(debug)
73 | ndk {
74 | abiFilters getNDKAbi('arm')
75 | }
76 | }
77 | armRelease {
78 | initWith(release)
79 | ndk {
80 | abiFilters getNDKAbi('arm')
81 | }
82 | }
83 | armv7Debug {
84 | initWith(debug)
85 | ndk {
86 | abiFilters getNDKAbi('armv7')
87 | }
88 | }
89 | armv7Release {
90 | initWith(release)
91 | ndk {
92 | abiFilters getNDKAbi('armv7')
93 | }
94 | }
95 | }
96 |
97 | // Ignore default 'debug' and 'release' build types
98 | variantFilter { variant ->
99 | if(variant.buildType.name.equals('release') || variant.buildType.name.equals('debug')) {
100 | variant.setIgnore(true);
101 | }
102 | }
103 |
104 | // Define apk output directory
105 | applicationVariants.all { variant ->
106 | variant.outputs.each { output ->
107 | def name = variant.buildType.name
108 | output.outputFile = new File(getApkPath(isDebug(name), getArch(name)))
109 | }
110 | }
111 | }
112 |
113 | task appStart(type: Exec, dependsOn: 'installArmRelease') {
114 | // linux
115 | commandLine 'adb', 'shell', 'am', 'start', '-n', 'com.rust.webvr/.MainActivity'
116 |
117 | // windows
118 | // commandLine 'cmd', '/c', 'adb', 'shell', 'am', 'start', '-n', 'com.example/.MyActivity'
119 | }
120 |
121 | dependencies {
122 | daydreamCompile project(':GVRService')
123 | gearvrCompile project(':OVRService')
124 | }
125 |
126 |
127 | String getArch(String buildType) {
128 | return buildType.replaceAll(/(Debug|Release)/, '')
129 | }
130 |
131 |
132 | boolean isDebug(String buildType) {
133 | return buildType.contains("Debug")
134 | }
135 |
136 | String getTargetDir(boolean debug, String arch) {
137 | def basePath = project.rootDir.getParentFile().absolutePath
138 | debug = false;
139 | return basePath + '/target/' + getRustTarget(arch) + '/' + (debug ? 'debug' : 'release')
140 | }
141 |
142 | String getApkPath(boolean debug, String arch) {
143 | return getTargetDir(debug, arch) + '/servo.apk'
144 | }
145 |
146 | def getJniLibsPath(boolean debug, String arch) {
147 | return [
148 | getTargetDir(debug, arch) + "/libs",
149 | "../libs"
150 | ]
151 | }
152 |
153 | String getRustTarget(String arch) {
154 | switch (arch.toLowerCase()) {
155 | case 'arm' : return 'arm-linux-androideabi'
156 | case 'armv7' : return 'armv7-linux-androideabi'
157 | case 'arm64' : return 'aarch64-linux-android'
158 | case 'x86' : return 'x86'
159 | default: throw new GradleException("Invalid target architecture " + arch)
160 | }
161 | }
162 |
163 | String getNDKAbi(String arch) {
164 | switch (arch.toLowerCase()) {
165 | case 'arm' : return 'armeabi'
166 | case 'armv7' : return 'armeabi-v7a'
167 | case 'arm64' : return 'arm64-v8a'
168 | case 'x86' : return 'x86'
169 | default: throw new GradleException("Invalid target architecture " + arch)
170 | }
171 | }
--------------------------------------------------------------------------------
/rust-webvr/src/api/oculusvr/gradle/src/main/java/com/rust/webvr/OVRService.java:
--------------------------------------------------------------------------------
1 | package com.rust.webvr;
2 |
3 | import android.app.Activity;
4 | import android.app.Application;
5 | import android.os.Bundle;
6 | import android.util.Log;
7 | import android.view.Surface;
8 | import android.view.SurfaceHolder;
9 | import android.view.SurfaceView;
10 | import android.widget.FrameLayout;
11 | import android.widget.FrameLayout.LayoutParams;
12 |
13 | class OVRService implements Application.ActivityLifecycleCallbacks, SurfaceHolder.Callback {
14 | private Activity mActivity;
15 | private SurfaceView mSurfaceView;
16 | private long mPtr = 0; // Native Rustlang struct pointer
17 | private boolean mPresenting = false;
18 |
19 | private static native void nativeOnPause(long ptr);
20 | private static native void nativeOnResume(long ptr);
21 | private static native void nativeOnSurfaceChanged(long ptr, Surface surface);
22 | private static native void nativeOnSurfaceDestroyed(long ptr);
23 |
24 | void init(final Activity activity, long ptr) {
25 | mActivity = activity;
26 | mPtr = ptr;
27 |
28 | Runnable initOvr = new Runnable() {
29 | @Override
30 | public void run() {
31 | mSurfaceView = new SurfaceView(activity);
32 | mSurfaceView.getHolder().addCallback(OVRService.this);
33 | // Enabling setZOrderOnTop is very important! If not enabled a simple swap_buffers
34 | // on the window will make the SurfaceView invisible
35 | mSurfaceView.setZOrderOnTop(true);
36 | activity.getApplication().registerActivityLifecycleCallbacks(OVRService.this);
37 |
38 | // Wait until completed
39 | synchronized(this) {
40 | this.notify();
41 | }
42 | }
43 | };
44 |
45 | synchronized (initOvr) {
46 | activity.runOnUiThread(initOvr);
47 | try {
48 | initOvr.wait();
49 | }
50 | catch (Exception ex) {
51 | Log.e("rust-webvr", Log.getStackTraceString(ex));
52 | }
53 | }
54 | }
55 |
56 | // Called from JNI
57 | public static Object create(Activity activity, long ptr) {
58 | OVRService service = new OVRService();
59 | service.init(activity, ptr);
60 | return service;
61 | }
62 |
63 | // Called from Native
64 | public void startPresent() {
65 | mActivity.runOnUiThread(new Runnable() {
66 | @Override
67 | public void run() {
68 | if (mPresenting) {
69 | return;
70 | }
71 | // Show SurfaceView
72 | FrameLayout rootLayout = (FrameLayout) mActivity.findViewById(android.R.id.content);
73 | rootLayout.addView(mSurfaceView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
74 |
75 | mPresenting = true;
76 | }
77 | });
78 | }
79 |
80 | public void stopPresent() {
81 | mActivity.runOnUiThread(new Runnable() {
82 | @Override
83 | public void run() {
84 | if (!mPresenting) {
85 | return;
86 | }
87 | mPresenting = false;
88 | // Hide SurfaceView
89 | FrameLayout rootLayout = (FrameLayout)mActivity.findViewById(android.R.id.content);
90 | rootLayout.removeView(mSurfaceView);
91 | }
92 | });
93 | }
94 |
95 | // ActivityLifecycleCallbacks
96 | @Override
97 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
98 |
99 | }
100 |
101 | @Override
102 | public void onActivityStarted(Activity activity) {
103 |
104 | }
105 |
106 | @Override
107 | public void onActivityResumed(Activity activity) {
108 | if (activity != mActivity) {
109 | return;
110 | }
111 | nativeOnResume(mPtr);
112 | }
113 |
114 | @Override
115 | public void onActivityPaused(Activity activity) {
116 | if (activity != mActivity) {
117 | return;
118 | }
119 | nativeOnPause(mPtr);
120 | }
121 |
122 | @Override
123 | public void onActivityStopped(Activity activity) {
124 |
125 | }
126 |
127 | @Override
128 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
129 |
130 | }
131 |
132 | @Override
133 | public void onActivityDestroyed(Activity activity) {
134 | if (mActivity == activity) {
135 | activity.getApplication().unregisterActivityLifecycleCallbacks(this);
136 | mActivity = null; // Don't leak activity
137 | }
138 | }
139 |
140 | // SurfaceView Callbacks
141 | @Override
142 | public void surfaceCreated(SurfaceHolder holder) {
143 | // No Op
144 | }
145 |
146 | @Override
147 | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
148 | nativeOnSurfaceChanged(mPtr, holder.getSurface());
149 | }
150 |
151 | @Override
152 | public void surfaceDestroyed(SurfaceHolder holder) {
153 | nativeOnSurfaceDestroyed(mPtr);
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/examples/room/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/googlevr/gradle/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/oculusvr/gradle/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/rust-webvr-api/src/utils.rs:
--------------------------------------------------------------------------------
1 | #[cfg(feature = "utils")]
2 |
3 | use std::mem;
4 | use std::sync::atomic::AtomicUsize;
5 | use std::sync::atomic::Ordering::SeqCst;
6 | use time;
7 |
8 | static DEVICE_ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
9 |
10 | // Generates a unique identifier for any VRDisplay
11 | #[allow(dead_code)]
12 | pub fn new_id() -> u32 {
13 | DEVICE_ID_COUNTER.fetch_add(1, SeqCst) as u32
14 | }
15 |
16 | // Returns the current time in milliseconds
17 | #[allow(dead_code)]
18 | pub fn timestamp() -> f64 {
19 | let timespec = time::get_time();
20 | timespec.sec as f64 * 1000.0 + (timespec.nsec as f64 * 1e-6)
21 | }
22 |
23 | // Multiply 4x4 matrices
24 | #[allow(dead_code)]
25 | pub fn multiply_matrix(a: &[f32; 16], b: &[f32; 16], out: &mut [f32; 16]) {
26 | let mut tmp: [f32; 16] = [0.; 16];
27 |
28 | tmp[0] = b[0] * a[0] + b[1] * a[4] + b[2] * a[8] + b[3] * a[12];
29 | tmp[1] = b[0] * a[1] + b[1] * a[5] + b[2] * a[9] + b[3] * a[13];
30 | tmp[2] = b[0] * a[2] + b[1] * a[6] + b[2] * a[10] + b[3] * a[14];
31 | tmp[3] = b[0] * a[3] + b[1] * a[7] + b[2] * a[11] + b[3] * a[15];
32 |
33 | tmp[4] = b[4] * a[0] + b[5] * a[4] + b[6] * a[8] + b[7] * a[12];
34 | tmp[5] = b[4] * a[1] + b[5] * a[5] + b[6] * a[9] + b[7] * a[13];
35 | tmp[6] = b[4] * a[2] + b[5] * a[6] + b[6] * a[10] + b[7] * a[14];
36 | tmp[7] = b[4] * a[3] + b[5] * a[7] + b[6] * a[11] + b[7] * a[15];
37 |
38 | tmp[8] = b[8] * a[0] + b[9] * a[4] + b[10] * a[8] + b[11] * a[12];
39 | tmp[9] = b[8] * a[1] + b[9] * a[5] + b[10] * a[9] + b[11] * a[13];
40 | tmp[10] = b[8] * a[2] + b[9] * a[6] + b[10] * a[10] + b[11] * a[14];
41 | tmp[11] = b[8] * a[3] + b[9] * a[7] + b[10] * a[11] + b[11] * a[15];
42 |
43 | tmp[12] = b[12] * a[0] + b[13] * a[4] + b[14] * a[8] + b[15] * a[12];
44 | tmp[13] = b[12] * a[1] + b[13] * a[5] + b[14] * a[9] + b[15] * a[13];
45 | tmp[14] = b[12] * a[2] + b[13] * a[6] + b[14] * a[10] + b[15] * a[14];
46 | tmp[15] = b[12] * a[3] + b[13] * a[7] + b[14] * a[11] + b[15] * a[15];
47 |
48 | *out = tmp;
49 | }
50 |
51 | #[allow(dead_code)]
52 | pub fn inverse_matrix(m: &[f32; 16], out: &mut [f32; 16]) -> bool {
53 | adjoint_matrix(&m, out);
54 |
55 | let det = determinant4x4(m);
56 | if det == 0f32 {
57 | return false;
58 | }
59 |
60 | for i in 0..16 {
61 | out[i] = out[i] / det;
62 | }
63 | true
64 | }
65 |
66 | #[allow(dead_code)]
67 | pub fn adjoint_matrix(m: &[f32; 16], out: &mut [f32; 16]) {
68 | let mut tmp: [f32; 16] = [0.; 16];
69 |
70 | tmp[0] = determinant3x3(m[5], m[9], m[13], m[6], m[10], m[14], m[7], m[11], m[15]);
71 | tmp[4] = - determinant3x3(m[4], m[8], m[12], m[6], m[10], m[14], m[7], m[11], m[15]);
72 | tmp[8] = determinant3x3(m[4], m[8], m[12], m[5], m[9], m[13], m[7], m[11], m[15]);
73 | tmp[12] = - determinant3x3(m[4], m[8], m[12], m[5], m[9], m[13], m[6], m[10], m[14]);
74 |
75 | tmp[1] = - determinant3x3(m[1], m[9], m[13], m[2], m[10], m[14], m[3], m[11], m[15]);
76 | tmp[5] = determinant3x3(m[0], m[8], m[12], m[2], m[10], m[14], m[3], m[11], m[15]);
77 | tmp[9] = - determinant3x3(m[0], m[8], m[12], m[1], m[9], m[13], m[3], m[11], m[15]);
78 | tmp[13] = determinant3x3(m[0], m[8], m[12], m[1], m[9], m[13], m[2], m[10], m[14]);
79 |
80 | tmp[2] = determinant3x3(m[1], m[5], m[13], m[2], m[6], m[14], m[3], m[7], m[15]);
81 | tmp[6] = - determinant3x3(m[0], m[4], m[12], m[2], m[6], m[14], m[3], m[7], m[15]);
82 | tmp[10] = determinant3x3(m[0], m[4], m[12], m[1], m[5], m[13], m[3], m[7], m[15]);
83 | tmp[14] = - determinant3x3(m[0], m[4], m[12], m[1], m[5], m[13], m[2], m[6], m[14]);
84 |
85 | tmp[3] = - determinant3x3(m[1], m[5], m[9], m[2], m[6], m[10], m[3], m[7], m[11]);
86 | tmp[7] = determinant3x3(m[0], m[4], m[8], m[2], m[6], m[10], m[3], m[7], m[11]);
87 | tmp[11] = - determinant3x3(m[0], m[4], m[8], m[1], m[5], m[9], m[3], m[7], m[11]);
88 | tmp[15] = determinant3x3(m[0], m[4], m[8], m[1], m[5], m[9], m[2], m[6], m[10]);
89 |
90 | *out = tmp;
91 | }
92 |
93 | #[allow(dead_code)]
94 | pub fn determinant4x4(m: &[f32; 16]) -> f32 {
95 | m[0] * determinant3x3(m[5], m[9], m[13], m[6], m[10], m[14], m[7], m[11], m[15])
96 | - m[1] * determinant3x3(m[4], m[8], m[12], m[6], m[10], m[14], m[7], m[11], m[15])
97 | + m[2] * determinant3x3(m[4], m[8], m[12], m[5], m[9], m[13], m[7], m[11], m[15])
98 | - m[3] * determinant3x3(m[4], m[8], m[12], m[5], m[9], m[13], m[6], m[10], m[14])
99 | }
100 |
101 | #[allow(dead_code)]
102 | fn determinant3x3(a1: f32, a2: f32, a3: f32, b1: f32, b2: f32, b3: f32, c1: f32, c2: f32, c3: f32) -> f32 {
103 | a1 * determinant2x2(b2, b3, c2, c3)
104 | - b1 * determinant2x2(a2, a3, c2, c3)
105 | + c1 * determinant2x2(a2, a3, b2, b3)
106 | }
107 |
108 | #[allow(dead_code)]
109 | #[inline]
110 | fn determinant2x2(a: f32, b: f32, c: f32, d: f32) -> f32 {
111 | a * d - b * c
112 | }
113 |
114 | // Adapted from http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
115 | #[allow(dead_code)]
116 | #[inline]
117 | pub fn matrix_to_quat(matrix: &[f32; 16]) -> [f32; 4] {
118 | let m: &[[f32; 4]; 4] = unsafe { mem::transmute(matrix) };
119 | let w = f32::max(0.0, 1.0 + m[0][0] + m[1][1] + m[2][2]).sqrt() * 0.5;
120 | let mut x = f32::max(0.0, 1.0 + m[0][0] - m[1][1] - m[2][2]).sqrt() * 0.5;
121 | let mut y = f32::max(0.0, 1.0 - m[0][0] + m[1][1] - m[2][2]).sqrt() * 0.5;
122 | let mut z = f32::max(0.0, 1.0 - m[0][0] - m[1][1] + m[2][2]).sqrt() * 0.5;
123 |
124 | x = copysign(x, m[2][1] - m[1][2]);
125 | y = copysign(y, m[0][2] - m[2][0]);
126 | z = copysign(z, m[1][0] - m[0][1]);
127 |
128 | [x, y, z, w]
129 | }
130 |
131 | #[allow(dead_code)]
132 | #[inline]
133 | pub fn copysign(a: f32, b: f32) -> f32 {
134 | if b == 0.0 {
135 | 0.0
136 | } else {
137 | a.abs() * b.signum()
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/googlevr/gamepad.rs:
--------------------------------------------------------------------------------
1 | #![cfg(feature = "googlevr")]
2 | use {VRGamepad, VRGamepadData, VRGamepadHand, VRGamepadState, VRGamepadButton};
3 | use rust_webvr_api::utils;
4 | use gvr_sys as gvr;
5 | use gvr_sys::gvr_controller_api_status::*;
6 | use gvr_sys::gvr_controller_button::*;
7 | use gvr_sys::gvr_controller_connection_state::*;
8 | use gvr_sys::gvr_controller_handedness::*;
9 | use std::cell::RefCell;
10 | use std::mem;
11 | use std::ffi::CStr;
12 | use std::sync::Arc;
13 |
14 | pub type GoogleVRGamepadPtr = Arc>;
15 |
16 | pub struct GoogleVRGamepad {
17 | ctx: *mut gvr::gvr_context,
18 | controller_ctx: *mut gvr::gvr_controller_context,
19 | state: *mut gvr::gvr_controller_state,
20 | gamepad_id: u32,
21 | display_id: u32,
22 | paused: bool,
23 | system_paused: bool,
24 | }
25 |
26 | unsafe impl Send for GoogleVRGamepad {}
27 | unsafe impl Sync for GoogleVRGamepad {}
28 |
29 | impl GoogleVRGamepad {
30 | pub unsafe fn new(ctx: *mut gvr::gvr_context,
31 | controller_ctx: *mut gvr::gvr_controller_context,
32 | display_id: u32)
33 | -> Result>, String> {
34 | let gamepad = Self {
35 | ctx: ctx,
36 | controller_ctx: controller_ctx,
37 | state: gvr::gvr_controller_state_create(),
38 | gamepad_id: utils::new_id(),
39 | display_id: display_id,
40 | paused: false,
41 | system_paused: false,
42 | };
43 | gvr::gvr_controller_state_update(controller_ctx, 0, gamepad.state);
44 | let api_status = gvr::gvr_controller_state_get_api_status(gamepad.state);
45 | if api_status != GVR_CONTROLLER_API_OK as i32 {
46 | let message = CStr::from_ptr(gvr::gvr_controller_api_status_to_string(api_status));
47 | return Err(message.to_string_lossy().into());
48 | }
49 |
50 | Ok(Arc::new(RefCell::new(gamepad)))
51 | }
52 |
53 | // Warning: this function is called from java Main thread
54 | // The action it's handled in handle_events method for thread safety
55 | #[allow(dead_code)]
56 | pub fn pause(&mut self) {
57 | self.system_paused = true;
58 | }
59 |
60 | // Warning: this function is called from java Main thread
61 | // The action it's handled in handle_events method for thread safety
62 | #[allow(dead_code)]
63 | pub fn resume(&mut self) {
64 | self.system_paused = false;
65 | }
66 |
67 | pub fn handle_events(&mut self) {
68 | if self.system_paused == self.paused {
69 | return;
70 | }
71 | self.paused = self.system_paused;
72 | unsafe {
73 | if self.paused {
74 | gvr::gvr_controller_pause(self.controller_ctx);
75 | }
76 | else {
77 | gvr::gvr_controller_resume(self.controller_ctx);
78 | }
79 | }
80 | }
81 | }
82 |
83 | impl Drop for GoogleVRGamepad {
84 | fn drop(&mut self) {
85 | unsafe {
86 | gvr::gvr_controller_state_destroy(mem::transmute(&self.state));
87 | }
88 | }
89 | }
90 |
91 | impl VRGamepad for GoogleVRGamepad {
92 | fn id(&self) -> u32 {
93 | self.gamepad_id
94 | }
95 |
96 | fn data(&self) -> VRGamepadData {
97 | let handeness = unsafe {
98 | let prefs = gvr::gvr_get_user_prefs(self.ctx);
99 | gvr::gvr_user_prefs_get_controller_handedness(prefs)
100 | };
101 | let hand = if handeness == GVR_CONTROLLER_LEFT_HANDED as i32 {
102 | VRGamepadHand::Left
103 | } else {
104 | VRGamepadHand::Right
105 | };
106 |
107 | VRGamepadData {
108 | display_id: self.display_id,
109 | name: "Daydream Controller".into(),
110 | hand: hand
111 | }
112 | }
113 |
114 | fn state(&self) -> VRGamepadState {
115 | let mut out = VRGamepadState::default();
116 |
117 | out.gamepad_id = self.gamepad_id;
118 | unsafe {
119 | gvr::gvr_controller_state_update(self.controller_ctx, 0, self.state);
120 | let connection_state = gvr::gvr_controller_state_get_connection_state(self.state);
121 | out.connected = connection_state == GVR_CONTROLLER_CONNECTED as i32;
122 |
123 | let touchpad_touching = gvr::gvr_controller_state_is_touching(self.state);
124 |
125 | // Touchpad: (0,0) is the top-left of the touchpad and (1,1)
126 | // Map to -1 1 for each axis.
127 | let pos = gvr::gvr_controller_state_get_touch_pos(self.state);
128 | out.axes = if touchpad_touching {
129 | [pos.x as f64 * 2.0 - 1.0,
130 | pos.y as f64 * 2.0 - 1.0].to_vec()
131 | } else {
132 | [0.0, 0.0].to_vec()
133 | };
134 |
135 | // Add touchpad as a button
136 | out.buttons.push(VRGamepadButton {
137 | pressed: gvr::gvr_controller_state_get_button_state(self.state, GVR_CONTROLLER_BUTTON_CLICK as i32),
138 | touched: touchpad_touching,
139 | });
140 |
141 | // Extra buttons
142 | let buttons = [GVR_CONTROLLER_BUTTON_HOME,
143 | GVR_CONTROLLER_BUTTON_APP,
144 | GVR_CONTROLLER_BUTTON_VOLUME_UP,
145 | GVR_CONTROLLER_BUTTON_VOLUME_DOWN];
146 | for button in &buttons {
147 | let pressed = gvr::gvr_controller_state_get_button_state(self.state, *button as i32);
148 | out.buttons.push(VRGamepadButton {
149 | pressed: pressed,
150 | touched: pressed,
151 | });
152 | }
153 |
154 | let quat = gvr::gvr_controller_state_get_orientation(self.state);
155 | out.pose.orientation = Some([
156 | quat.qx, quat.qy, quat.qz, quat.qw
157 | ]);
158 |
159 | let acc = gvr::gvr_controller_state_get_accel(self.state);
160 | out.pose.linear_acceleration = Some([
161 | acc.x, acc.y, acc.z
162 | ]);
163 |
164 | let vel = gvr::gvr_controller_state_get_gyro(self.state);
165 | out.pose.angular_velocity = Some([
166 | vel.x, vel.y, vel.z
167 | ]);
168 | }
169 |
170 | out
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/googlevr/gradle/src/main/java/com/rust/webvr/GVRService.java:
--------------------------------------------------------------------------------
1 | package com.rust.webvr;
2 |
3 | import android.app.Activity;
4 | import android.app.Application;
5 | import android.content.pm.ActivityInfo;
6 | import android.os.Bundle;
7 | import android.util.Log;
8 | import android.view.View;
9 | import android.view.WindowManager;
10 | import android.widget.FrameLayout;
11 | import android.widget.FrameLayout.LayoutParams;
12 |
13 | import com.google.vr.ndk.base.AndroidCompat;
14 | import com.google.vr.ndk.base.GvrLayout;
15 |
16 | class GVRService implements Application.ActivityLifecycleCallbacks {
17 | private Activity mActivity;
18 | private GvrLayout gvrLayout;
19 | private long mPtr = 0; // Native Rustlang struct pointer
20 | private boolean mPresenting = false;
21 | private boolean mPaused = false;
22 | private boolean mGvrResumed = false;
23 |
24 | private static native void nativeOnPause(long ptr);
25 | private static native void nativeOnResume(long ptr);
26 |
27 | void init(final Activity activity, long ptr) {
28 | mActivity = activity;
29 | mPtr = ptr;
30 |
31 | Runnable initGvr = new Runnable() {
32 | @Override
33 | public void run() {
34 | gvrLayout = new GvrLayout(activity);
35 | // Decouple the app framerate from the display framerate
36 | if (gvrLayout.setAsyncReprojectionEnabled(true)) {
37 | // Android N hint to tune apps for a predictable,
38 | // consistent level of device performance over long periods of time.
39 | // The system automatically disables this mode when the window
40 | // is no longer in focus.
41 | AndroidCompat.setSustainedPerformanceMode(activity, true);
42 | }
43 | gvrLayout.setPresentationView(new View(activity));
44 |
45 | activity.getApplication().registerActivityLifecycleCallbacks(GVRService.this);
46 |
47 | // Wait until completed
48 | synchronized(this) {
49 | this.notify();
50 | }
51 | }
52 | };
53 |
54 | synchronized (initGvr) {
55 | activity.runOnUiThread(initGvr);
56 | try {
57 | initGvr.wait();
58 | }
59 | catch (Exception ex) {
60 | Log.e("rust-webvr", Log.getStackTraceString(ex));
61 | }
62 | }
63 | }
64 |
65 | // Called from Native
66 | public long getNativeContext() {
67 | return gvrLayout.getGvrApi().getNativeGvrContext();
68 | }
69 |
70 | private void start() {
71 | if (mPresenting) {
72 | return;
73 | }
74 |
75 | mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
76 | mActivity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
77 | if (!AndroidCompat.setVrModeEnabled(mActivity, true)) {
78 | Log.w("rust-webvr", "setVrModeEnabled failed");
79 | }
80 |
81 | // Show GvrLayout
82 | FrameLayout rootLayout = (FrameLayout)mActivity.findViewById(android.R.id.content);
83 | rootLayout.addView(gvrLayout, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
84 |
85 | if (!mGvrResumed) {
86 | gvrLayout.onResume();
87 | mGvrResumed = true;
88 | }
89 | mPresenting = true;
90 | }
91 |
92 |
93 | // Called from Native
94 | public void startPresent() {
95 | mActivity.runOnUiThread(new Runnable() {
96 | @Override
97 | public void run() {
98 | start();
99 | }
100 | });
101 | }
102 |
103 | public void stopPresent() {
104 | mActivity.runOnUiThread(new Runnable() {
105 | @Override
106 | public void run() {
107 | if (!mPresenting) {
108 | return;
109 | }
110 | mPresenting = false;
111 | // Hide GvrLayout
112 | FrameLayout rootLayout = (FrameLayout)mActivity.findViewById(android.R.id.content);
113 | rootLayout.removeView(gvrLayout);
114 |
115 | AndroidCompat.setVrModeEnabled(mActivity, false);
116 | mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
117 | mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
118 | }
119 | });
120 | }
121 |
122 | // Called from JNI
123 | public static Object create(Activity activity, long ptr) {
124 | GVRService service = new GVRService();
125 | service.init(activity, ptr);
126 | return service;
127 | }
128 |
129 | // ActivityLifecycleCallbacks
130 | @Override
131 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
132 |
133 | }
134 |
135 | @Override
136 | public void onActivityStarted(Activity activity) {
137 | if (activity != mActivity) {
138 | return;
139 | }
140 | if (mPaused && gvrLayout != null && !mGvrResumed) {
141 | gvrLayout.onResume();
142 | mGvrResumed = true;
143 | mPaused = false;
144 | nativeOnResume(mPtr);
145 | }
146 | }
147 |
148 | @Override
149 | public void onActivityResumed(Activity activity) {
150 |
151 | }
152 |
153 | @Override
154 | public void onActivityPaused(Activity activity) {
155 |
156 | }
157 |
158 | @Override
159 | public void onActivityStopped(Activity activity) {
160 | if (activity != mActivity) {
161 | return;
162 | }
163 |
164 | if (mPresenting && gvrLayout != null && mGvrResumed) {
165 | gvrLayout.onPause();
166 | mGvrResumed = false;
167 | mPaused = true;
168 | nativeOnPause(mPtr);
169 | }
170 | }
171 |
172 | @Override
173 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
174 |
175 | }
176 |
177 | @Override
178 | public void onActivityDestroyed(Activity activity) {
179 | if (mActivity == activity) {
180 | if (gvrLayout != null) {
181 | gvrLayout.shutdown();
182 | gvrLayout = null;
183 | }
184 | activity.getApplication().unregisterActivityLifecycleCallbacks(this);
185 | mActivity = null; // Don't leak activity
186 | }
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/rust-webvr/src/vr_manager.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 | use VRDisplayPtr;
3 | use VREvent;
4 | use VRGamepadPtr;
5 | use VRService;
6 | use VRServiceCreator;
7 |
8 | #[cfg(target_os = "android")]
9 | #[cfg(feature = "googlevr")]
10 | use api::GoogleVRServiceCreator;
11 |
12 | #[cfg(target_os = "windows")]
13 | #[cfg(feature = "openvr")]
14 | use api::OpenVRServiceCreator;
15 |
16 | #[cfg(target_os = "android")]
17 | #[cfg(feature = "oculusvr")]
18 | use api::OculusVRServiceCreator;
19 |
20 | #[cfg(feature = "mock")]
21 | use api::{MockServiceCreator, MockVRControlMsg, MockVRInit};
22 |
23 | #[cfg(feature = "vrexternal")]
24 | use api::VRExternalShmemPtr;
25 |
26 | #[cfg(target_os = "android")]
27 | #[cfg(feature = "vrexternal")]
28 | use api::VRExternalServiceCreator;
29 |
30 | // Single entry point all the VRServices and displays
31 | pub struct VRServiceManager {
32 | initialized: bool,
33 | services: Vec>,
34 | displays: HashMap,
35 | gamepads: HashMap
36 | }
37 |
38 | unsafe impl Send for VRServiceManager {}
39 |
40 | impl Drop for VRServiceManager {
41 | fn drop(&mut self) {
42 | self.gamepads.clear();
43 | self.displays.clear();
44 | self.services.clear();
45 | }
46 | }
47 |
48 | impl VRServiceManager {
49 | pub fn new() -> VRServiceManager {
50 | VRServiceManager {
51 | initialized: false,
52 | services: Vec::new(),
53 | displays: HashMap::new(),
54 | gamepads: HashMap::new()
55 | }
56 | }
57 |
58 | // Register default VR services specified in crate's features
59 | pub fn register_defaults(&mut self) {
60 | let creators: Vec> = vec!(
61 | #[cfg(target_os = "windows")]
62 | #[cfg(feature = "openvr")]
63 | OpenVRServiceCreator::new(),
64 | #[cfg(target_os = "android")]
65 | #[cfg(feature = "googlevr")]
66 | GoogleVRServiceCreator::new(),
67 | #[cfg(target_os = "android")]
68 | #[cfg(feature = "oculusvr")]
69 | OculusVRServiceCreator::new(),
70 | );
71 |
72 | for creator in &creators {
73 | self.register(creator.new_service());
74 | }
75 | }
76 |
77 | // Register VRExternal service.
78 | #[cfg(target_os = "android")]
79 | #[cfg(feature = "vrexternal")]
80 | pub fn register_vrexternal(&mut self, ptr: VRExternalShmemPtr) {
81 | let creator = VRExternalServiceCreator::new(ptr);
82 | self.register(creator.new_service());
83 | }
84 |
85 | // Register VRExternal service.
86 | #[cfg(not(target_os = "android"))]
87 | #[cfg(feature = "vrexternal")]
88 | pub fn register_vrexternal(&mut self, _: VRExternalShmemPtr) {
89 | unimplemented!();
90 | }
91 |
92 | // Register mock VR Service
93 | // Usefull for testing
94 | #[cfg(feature = "mock")]
95 | pub fn register_mock(&mut self) {
96 | let creator = MockServiceCreator::new();
97 | self.register(creator.new_service());
98 | }
99 |
100 | // Register mock VR Service
101 | // Usefull for testing
102 | #[cfg(feature = "mock")]
103 | pub fn register_mock_with_remote(&mut self, init: MockVRInit) -> std::sync::mpsc::Sender {
104 | let (service, remote) = MockServiceCreator::new_service_with_remote(init);
105 | self.register(service);
106 | remote
107 | }
108 |
109 | // Register a new VR service
110 | pub fn register(&mut self, service: Box) {
111 | self.services.push(service);
112 | }
113 |
114 | // Initializes all the services
115 | pub fn initialize_services(&mut self) {
116 | if self.initialized {
117 | return;
118 | }
119 |
120 | for service in &mut self.services {
121 | if let Err(msg) = service.initialize() {
122 | error!("Error initializing VRService: {:?}", msg);
123 | }
124 | }
125 | self.initialized = true;
126 | }
127 |
128 | pub fn get_displays(&mut self) -> Vec {
129 | self.fetch_displays();
130 | let mut result = Vec::new();
131 | for (_, display) in &self.displays {
132 | result.push(display.clone());
133 | }
134 | // Sort by display_id to match service initialization order
135 | result.sort_by(|a, b| a.borrow().id().cmp(&b.borrow().id()));
136 | result
137 | }
138 |
139 | pub fn get_gamepads(&mut self) -> Vec {
140 | self.fetch_gamepads();
141 | let mut result = Vec::new();
142 | for (_, gamepad) in &self.gamepads {
143 | result.push(gamepad.clone());
144 | }
145 | // Sort by gamepad_id to match service initialization order
146 | result.sort_by(|a, b| a.borrow().id().cmp(&b.borrow().id()));
147 | result
148 | }
149 |
150 | pub fn get_display(&self, display_id: u32) -> Option<&VRDisplayPtr> {
151 | self.displays.get(&display_id)
152 | }
153 |
154 | pub fn poll_events(&mut self) -> Vec {
155 | let mut events = Vec::new();
156 | for service in &mut self.services {
157 | events.append(&mut service.poll_events());
158 | }
159 | events
160 | }
161 |
162 | pub fn is_initialized(&self) -> bool {
163 | self.initialized
164 | }
165 | }
166 |
167 | impl VRServiceManager {
168 | fn fetch_displays(&mut self) {
169 | self.initialize_services();
170 |
171 | for service in &mut self.services {
172 | let displays = service.fetch_displays();
173 | if let Ok(displays) = displays {
174 | for display in displays {
175 | let key = display.borrow().id();
176 | if !self.displays.contains_key(&key) {
177 | self.displays.insert(key, display.clone());
178 | }
179 | }
180 | }
181 | }
182 | }
183 |
184 | fn fetch_gamepads(&mut self) {
185 | self.initialize_services();
186 |
187 | for service in &mut self.services {
188 | let gamepads = service.fetch_gamepads();
189 | if let Ok(gamepads) = gamepads {
190 | for gamepad in gamepads {
191 | let key = gamepad.borrow().id();
192 | if !self.gamepads.contains_key(&key) {
193 | self.gamepads.insert(key, gamepad.clone());
194 | }
195 | }
196 | }
197 | }
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/googlevr/service.rs:
--------------------------------------------------------------------------------
1 | #![cfg(feature = "googlevr")]
2 |
3 | use {VRService, VRDisplay, VRDisplayPtr, VREvent, VRGamepadPtr};
4 | use super::display::{GoogleVRDisplay, GoogleVRDisplayPtr};
5 | #[cfg(target_os="android")]
6 | use rust_webvr_api::jni_utils::JNIScope;
7 | #[cfg(target_os="android")]
8 | use android_injected_glue::ffi as ndk;
9 | use gvr_sys as gvr;
10 | use std::mem;
11 | use std::ptr;
12 |
13 | #[cfg(target_os="android")]
14 | const SERVICE_CLASS_NAME:&'static str = "com/rust/webvr/GVRService";
15 |
16 | pub struct GoogleVRService {
17 | ctx: *mut gvr::gvr_context,
18 | controller_ctx: *mut gvr::gvr_controller_context,
19 | display: Option,
20 | #[cfg(target_os="android")]
21 | pub java_object: ndk::jobject,
22 | #[cfg(target_os="android")]
23 | pub java_class: ndk::jclass,
24 | }
25 |
26 | unsafe impl Send for GoogleVRService {}
27 |
28 | impl VRService for GoogleVRService {
29 | fn initialize(&mut self) -> Result<(), String> {
30 | if self.is_initialized() {
31 | return Ok(());
32 | }
33 |
34 | unsafe {
35 | try!(self.create_context());
36 | self.create_controller_context();
37 | }
38 |
39 | if self.ctx.is_null() {
40 | return Err("GoogleVR SDK failed to initialize".into());
41 | }
42 |
43 | Ok(())
44 | }
45 |
46 | fn fetch_displays(&mut self) -> Result,String> {
47 | let display = self.init_display()?;
48 | Ok(vec![display.clone()])
49 | }
50 |
51 | fn fetch_gamepads(&mut self) -> Result,String> {
52 | let display = self.init_display()?;
53 | display.borrow_mut().fetch_gamepads()
54 | }
55 |
56 | fn is_available(&self) -> bool {
57 | true
58 | }
59 |
60 | fn poll_events(&self) -> Vec {
61 | let mut events = Vec::new();
62 | if let Some(ref display) = self.display {
63 | let mut d = display.borrow_mut();
64 | d.poll_events(&mut events);
65 | if let Some(ref gp) = d.gamepad() {
66 | gp.borrow_mut().handle_events();
67 | }
68 | }
69 | events
70 | }
71 | }
72 |
73 | impl GoogleVRService {
74 | #[cfg(target_os="android")]
75 | pub fn new() -> GoogleVRService {
76 | GoogleVRService {
77 | ctx: ptr::null_mut(),
78 | controller_ctx: ptr::null_mut(),
79 | display: None,
80 | java_object: ptr::null_mut(),
81 | java_class: ptr::null_mut()
82 | }
83 | }
84 |
85 | #[cfg(not(target_os="android"))]
86 | pub fn new() -> GoogleVRService {
87 | GoogleVRService {
88 | ctx: ptr::null_mut(),
89 | controller_ctx: ptr::null_mut(),
90 | display: None,
91 | }
92 | }
93 |
94 | // On Android, the gvr_context must be be obtained from
95 | // the Java GvrLayout object via GvrLayout.getGvrApi().getNativeGvrContext()
96 | // Java code is implemented in GVRService. It handles the life cycle of the GvrLayout.
97 | // JNI code is used to comunicate with that Java code.
98 | #[cfg(target_os="android")]
99 | unsafe fn create_context(&mut self) -> Result<(), String> {
100 | let jni_scope = try!(JNIScope::attach());
101 |
102 | let jni = jni_scope.jni();
103 | let env = jni_scope.env;
104 |
105 | // Use NativeActivity's classloader to find our class
106 | self.java_class = try!(jni_scope.find_class(SERVICE_CLASS_NAME));
107 | if self.java_class.is_null() {
108 | return Err("Didn't find GVRService class".into());
109 | };
110 | self.java_class = (jni.NewGlobalRef)(env, self.java_class);
111 |
112 | // Create GVRService instance and own it as a globalRef.
113 | let method = jni_scope.get_method(self.java_class, "create", "(Landroid/app/Activity;J)Ljava/lang/Object;", true);
114 | let thiz: usize = mem::transmute(self as * mut GoogleVRService);
115 | self.java_object = (jni.CallStaticObjectMethod)(env, self.java_class, method, jni_scope.activity, thiz as ndk::jlong);
116 | if self.java_object.is_null() {
117 | return Err("Failed to create GVRService instance".into());
118 | };
119 | self.java_object = (jni.NewGlobalRef)(env, self.java_object);
120 |
121 | // Finally we have everything required to get the gvr_context pointer from java :)
122 | let method = jni_scope.get_method(self.java_class, "getNativeContext", "()J", false);
123 | let pointer = (jni.CallLongMethod)(env, self.java_object, method);
124 | self.ctx = pointer as *mut gvr::gvr_context;
125 | if self.ctx.is_null() {
126 | return Err("Failed to getNativeGvrContext from java GvrLayout".into());
127 | }
128 |
129 | Ok(())
130 | }
131 |
132 | #[cfg(not(target_os="android"))]
133 | unsafe fn create_context(&mut self) -> Result<(), String> {
134 | self.ctx = gvr::gvr_create();
135 | Ok(())
136 | }
137 |
138 | unsafe fn create_controller_context(&mut self) {
139 | let options = gvr::gvr_controller_get_default_options();
140 | self.controller_ctx = gvr::gvr_controller_create_and_init(options, self.ctx);
141 | gvr::gvr_controller_resume(self.controller_ctx);
142 | }
143 |
144 | fn is_initialized(&self) -> bool {
145 | return !self.ctx.is_null();
146 | }
147 |
148 | fn init_display(&mut self) -> Result<&GoogleVRDisplayPtr, String> {
149 | self.initialize()?;
150 |
151 | if let Some(ref d) = self.display {
152 | Ok(d)
153 | } else {
154 | self.display = unsafe { Some(GoogleVRDisplay::new(self, self.ctx, self.controller_ctx)) };
155 | Ok(self.display.as_ref().unwrap())
156 | }
157 | }
158 |
159 | // Called from Java main thread
160 | // Pause & resume methods are thread safe
161 | #[cfg(target_os="android")]
162 | fn on_pause(&mut self) {
163 | if let Some(ref display) = self.display {
164 | unsafe {
165 | (*display.as_ptr()).pause();
166 | }
167 | }
168 | }
169 |
170 | // Called from Java main thread
171 | // Pause & resume methods are thread safe
172 | #[cfg(target_os="android")]
173 | fn on_resume(&mut self) {
174 | if let Some(ref display) = self.display {
175 | unsafe {
176 | (*display.as_ptr()).resume();
177 | }
178 | }
179 | }
180 | }
181 |
182 | impl Drop for GoogleVRService {
183 | fn drop(&mut self) {
184 | if !self.controller_ctx.is_null() {
185 | unsafe {
186 | gvr::gvr_controller_destroy(mem::transmute(&self.ctx));
187 | }
188 | }
189 |
190 | if !self.ctx.is_null() {
191 | unsafe {
192 | gvr::gvr_destroy(mem::transmute(&self.ctx));
193 | }
194 | }
195 | }
196 | }
197 |
198 |
199 | #[cfg(target_os="android")]
200 | #[no_mangle]
201 | #[allow(non_snake_case)]
202 | #[allow(dead_code)]
203 | pub extern fn Java_com_rust_webvr_GVRService_nativeOnPause(_: *mut ndk::JNIEnv, service: ndk::jlong) {
204 | unsafe {
205 | let service: *mut GoogleVRService = mem::transmute(service as usize);
206 | (*service).on_pause();
207 | }
208 | }
209 |
210 | #[cfg(target_os="android")]
211 | #[no_mangle]
212 | #[allow(non_snake_case)]
213 | #[allow(dead_code)]
214 | pub extern fn Java_com_rust_webvr_GVRService_nativeOnResume(_: *mut ndk::JNIEnv, service: ndk::jlong) {
215 | unsafe {
216 | let service: *mut GoogleVRService = mem::transmute(service as usize);
217 | (*service).on_resume();
218 | }
219 | }
220 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/mock/display.rs:
--------------------------------------------------------------------------------
1 | use {VRDisplay, VRDisplayData, VRDisplayEvent, VREvent, VRFramebuffer, VRFramebufferAttributes, VRFrameData, VRGamepadPtr, VRStageParameters, VRLayer, VRViewport};
2 | use rust_webvr_api::utils;
3 | use std::sync::{Arc, Mutex};
4 | use std::cell::RefCell;
5 | use std::mem;
6 | pub type MockVRDisplayPtr = Arc>;
7 | use std::time::Duration;
8 | use std::thread;
9 | use super::{MockVRControlMsg, MockVRInit};
10 |
11 | pub struct MockVRDisplay {
12 | display_id: u32,
13 | attributes: VRFramebufferAttributes,
14 | state: Arc>,
15 | }
16 |
17 | pub struct MockVRState {
18 | display_data: VRDisplayData,
19 | frame_data: VRFrameData,
20 | events: Vec,
21 | }
22 |
23 | unsafe impl Send for MockVRDisplay {}
24 | unsafe impl Sync for MockVRDisplay {}
25 |
26 | impl MockVRDisplay {
27 | pub fn new(init: MockVRInit) -> MockVRDisplayPtr {
28 | let display_id = utils::new_id();
29 | Arc::new(RefCell::new(MockVRDisplay {
30 | display_id,
31 | attributes: Default::default(),
32 | state: Arc::new(Mutex::new(MockVRState::new(display_id, init))),
33 | }))
34 | }
35 |
36 | pub fn state_handle(&self) -> Arc> {
37 | self.state.clone()
38 | }
39 |
40 | pub fn poll_events(&self) -> Vec {
41 | let mut state = self.state.lock().unwrap();
42 | mem::replace(&mut state.events, vec![])
43 | }
44 | }
45 |
46 | impl VRDisplay for MockVRDisplay {
47 |
48 | fn id(&self) -> u32 {
49 | self.display_id
50 | }
51 |
52 | fn data(&self) -> VRDisplayData {
53 | self.state.lock().unwrap().display_data.clone()
54 | }
55 |
56 | fn immediate_frame_data(&self, _near_z: f64, _far_z: f64) -> VRFrameData {
57 | self.state.lock().unwrap().frame_data.clone()
58 | }
59 |
60 | fn synced_frame_data(&self, near_z: f64, far_z: f64) -> VRFrameData {
61 | self.immediate_frame_data(near_z, far_z)
62 | }
63 |
64 | fn reset_pose(&mut self) {
65 | // No op
66 | }
67 |
68 | fn sync_poses(&mut self) {
69 | // Simulate Vsync
70 | thread::sleep(Duration::from_millis(1));
71 | }
72 |
73 | fn bind_framebuffer(&mut self, _index: u32) {
74 | // No op
75 | }
76 |
77 | fn get_framebuffers(&self) -> Vec {
78 | vec![VRFramebuffer {
79 | eye_index: 0,
80 | attributes: self.attributes,
81 | viewport: VRViewport::new(0, 0, 1512/2, 1680)
82 | },
83 | VRFramebuffer {
84 | eye_index: 1,
85 | attributes: self.attributes,
86 | viewport: VRViewport::new(1512/2, 0, 1512/2, 1680)
87 | }]
88 | }
89 |
90 | fn render_layer(&mut self, _layer: &VRLayer) {
91 | // No op
92 | }
93 |
94 | fn fetch_gamepads(&mut self) -> Result,String> {
95 | Ok(Vec::new())
96 | }
97 |
98 | fn submit_frame(&mut self) {
99 | // No op
100 | }
101 |
102 | fn start_present(&mut self, attributes: Option) {
103 | if let Some(attributes) = attributes {
104 | self.attributes = attributes;
105 | }
106 | }
107 | }
108 |
109 | impl MockVRState {
110 | pub fn handle_msg(&mut self, msg: MockVRControlMsg) {
111 | match msg {
112 | MockVRControlMsg::SetViewerPose(position, orientation) => {
113 | self.frame_data.pose.position = Some(position);
114 | self.frame_data.pose.orientation = Some(orientation);
115 | }
116 | MockVRControlMsg::SetViews(left, right) => {
117 | self.display_data.left_eye_parameters.offset = left.offset;
118 | self.display_data.right_eye_parameters.offset = right.offset;
119 | self.frame_data.left_projection_matrix = left.projection;
120 | self.frame_data.right_projection_matrix = right.projection;
121 | self.events.push(VREvent::Display(VRDisplayEvent::Change(self.display_data.clone())))
122 | }
123 | MockVRControlMsg::SetEyeLevel(_eye) => {
124 | // do nothing for now
125 | }
126 | MockVRControlMsg::Focus => {
127 | self.events.push(VREvent::Display(VRDisplayEvent::Focus(self.display_data.clone())))
128 | }
129 | MockVRControlMsg::Blur => {
130 | self.events.push(VREvent::Display(VRDisplayEvent::Blur(self.display_data.clone())))
131 | }
132 | }
133 | }
134 | }
135 |
136 | impl MockVRState {
137 | pub fn new(display_id: u32, init: MockVRInit) -> Self {
138 | let mut display_data = VRDisplayData::default();
139 |
140 | // Mock display data
141 | // Simulates a virtual HTC Vive
142 |
143 | display_data.display_name = "Mock VRDisplay".into();
144 | display_data.display_id = display_id;
145 | display_data.connected = true;
146 |
147 | display_data.capabilities.can_present = true;
148 | display_data.capabilities.has_orientation = true;
149 | display_data.capabilities.has_external_display = true;
150 | display_data.capabilities.has_position = true;
151 |
152 | // todo use init.eye_level
153 | display_data.stage_parameters = Some(VRStageParameters {
154 | sitting_to_standing_transform: [-0.9317312, 0.0, 0.36314875, 0.0, 0.0, 0.99999994, 0.0, 0.0, -0.36314875,
155 | 0.0, -0.9317312, 0.0, 0.23767996, 1.6813644, 0.45370483, 1.0],
156 | size_x: 2.0,
157 | size_z: 2.0
158 | });
159 |
160 | display_data.left_eye_parameters.render_width = 1512;
161 | display_data.left_eye_parameters.render_height = 1680;
162 | display_data.left_eye_parameters.field_of_view.up_degrees = 55.82093048095703;
163 | display_data.left_eye_parameters.field_of_view.right_degrees = 51.26948547363281;
164 | display_data.left_eye_parameters.field_of_view.down_degrees = 55.707801818847656;
165 | display_data.left_eye_parameters.field_of_view.left_degrees = 54.42263412475586;
166 |
167 | display_data.right_eye_parameters.render_width = 1512;
168 | display_data.right_eye_parameters.render_height = 1680;
169 | display_data.right_eye_parameters.field_of_view.up_degrees = 55.898048400878906;
170 | display_data.right_eye_parameters.field_of_view.right_degrees = 54.37410354614258;
171 | display_data.right_eye_parameters.field_of_view.down_degrees = 55.614715576171875;
172 | display_data.right_eye_parameters.field_of_view.left_degrees = 51.304901123046875;
173 |
174 |
175 | let mut frame_data = VRFrameData::default();
176 |
177 | if let Some((position, orientation)) = init.viewer_origin {
178 | frame_data.pose.position = Some(position);
179 | frame_data.pose.orientation = Some(orientation);
180 | } else {
181 | // Position vector
182 | frame_data.pose.position = Some([0.5, -0.7, -0.3]);
183 | // Orientation quaternion
184 | // TODO: Add animation
185 | frame_data.pose.orientation = Some([0.9385081, -0.08066622, -0.3347714, 0.024972256]);
186 | }
187 |
188 | if let Some((left, right)) = init.views {
189 | display_data.left_eye_parameters.offset = left.offset;
190 | display_data.right_eye_parameters.offset = right.offset;
191 | frame_data.left_projection_matrix = left.projection;
192 | frame_data.right_projection_matrix = right.projection;
193 | } else {
194 | display_data.left_eye_parameters.offset = [0.035949998, 0.0, 0.015];
195 | display_data.right_eye_parameters.offset = [-0.035949998, 0.0, 0.015];
196 | // Simulates HTC Vive projections
197 | frame_data.left_projection_matrix = [0.75620246, 0.0, 0.0, 0.0,
198 | 0.0, 0.68050665, 0.0, 0.0,
199 | -0.05713458, -0.0021225351, -1.0000999, -1.0,
200 | 0.0, 0.0, -0.10000999, 0.0];
201 |
202 | frame_data.left_view_matrix = [1.0, 0.0, 0.0, 0.0,
203 | 0.0, 1.0, 0.0, 0.0,
204 | 0.0, 0.0, 1.0, 0.0,
205 | -0.035949998, 0.0, 0.015, 1.0];
206 |
207 | frame_data.right_projection_matrix = [0.75646526, 0.0, 0.0, 0.0,
208 | 0.0, 0.68069947, 0.0, 0.0,
209 | 0.055611316, -0.005315368, -1.0000999, -1.0,
210 | 0.0, 0.0, -0.10000999, 0.0];
211 |
212 | frame_data.right_view_matrix = [1.0, 0.0, 0.0, 0.0,
213 | 0.0, 1.0, 0.0, 0.0,
214 | 0.0, 0.0, 1.0, 0.0,
215 | 0.035949998, 0.0, 0.015, 1.0];
216 | };
217 |
218 |
219 | frame_data.timestamp = utils::timestamp();
220 |
221 | Self {
222 | display_data,
223 | frame_data,
224 | events: vec![]
225 | }
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/glwindow/heartbeat.rs:
--------------------------------------------------------------------------------
1 | use euclid::Angle;
2 | use euclid::default::RigidTransform3D;
3 | use euclid::Rotation3D;
4 | use euclid::Vector3D;
5 | use gleam::gl;
6 | use gleam::gl::Gl;
7 | use glutin::{WindowedContext, NotCurrent};
8 | use glutin::EventsLoop;
9 | use glutin::Event;
10 | use glutin::VirtualKeyCode;
11 | use glutin::WindowEvent;
12 | use rust_webvr_api::VRResolveFrameData;
13 | use rust_webvr_api::VRMainThreadHeartbeat;
14 | use std::rc::Rc;
15 | use std::time::Duration;
16 | use super::display::GlWindowVRDisplay;
17 | use super::service::EventsLoopFactory;
18 | use std::sync::Arc;
19 | use std::sync::mpsc::Receiver;
20 |
21 | const TIMEOUT: Duration = Duration::from_millis(16);
22 | const DELTA: f32 = 0.05;
23 | const ANGLE: Angle = Angle { radians: 0.1 };
24 |
25 | pub struct GlWindowVRMainThreadHeartbeat {
26 | receiver: Receiver,
27 | gl_context: Option>,
28 | events_loop_factory: EventsLoopFactory,
29 | events_loop: Option,
30 | gl: Rc,
31 | presenting: bool,
32 | timestamp: f64,
33 | texture_id: gl::GLuint,
34 | framebuffer_id: gl::GLuint,
35 | view: RigidTransform3D,
36 | }
37 |
38 | impl VRMainThreadHeartbeat for GlWindowVRMainThreadHeartbeat {
39 | fn heartbeat(&mut self) {
40 | debug!("VR heartbeat start");
41 | loop {
42 | // If we are presenting, we block the main thread on the VR thread.
43 | let msg = if self.presenting {
44 | self.receiver.recv_timeout(TIMEOUT).ok()
45 | } else {
46 | self.receiver.try_recv().ok()
47 | };
48 |
49 | match msg {
50 | Some(msg) => if self.handle_msg(msg) { break; },
51 | None => break,
52 | };
53 | }
54 | debug!("VR heartbeat stop");
55 | }
56 |
57 | fn heart_racing(&self) -> bool {
58 | self.presenting
59 | }
60 | }
61 |
62 | impl GlWindowVRMainThreadHeartbeat {
63 | pub(crate) fn new(
64 | receiver: Receiver,
65 | gl_context: WindowedContext,
66 | events_loop_factory: EventsLoopFactory,
67 | gl: Rc,
68 | ) -> GlWindowVRMainThreadHeartbeat {
69 | debug!("Creating VR heartbeat");
70 | GlWindowVRMainThreadHeartbeat {
71 | receiver: receiver,
72 | gl_context: Some(gl_context),
73 | events_loop_factory: events_loop_factory,
74 | events_loop: None,
75 | gl: gl,
76 | presenting: false,
77 | timestamp: 0.0,
78 | texture_id: 0,
79 | framebuffer_id: 0,
80 | view: RigidTransform3D::identity(),
81 | }
82 | }
83 |
84 | fn handle_msg(&mut self, msg: GlWindowVRMessage) -> bool {
85 | match msg {
86 | GlWindowVRMessage::StartPresenting => {
87 | debug!("VR starting");
88 | self.gl_context.as_ref().unwrap().window().show();
89 | self.presenting = true;
90 | if self.events_loop.is_none() {
91 | self.events_loop = (self.events_loop_factory)().ok();
92 | }
93 | true
94 | },
95 | GlWindowVRMessage::StartFrame(near, far, mut resolver) => {
96 | debug!("VR start frame");
97 | self.handle_window_events();
98 | let timestamp = self.timestamp;
99 | let window = self.gl_context.as_ref().unwrap().window();
100 | let size = window.get_inner_size().expect("No window size");
101 | let hidpi = window.get_hidpi_factor();
102 | let size = size.to_physical(hidpi);
103 | let view = self.view;
104 | let data = GlWindowVRDisplay::frame_data(timestamp, size, near, far, view);
105 | let _ = resolver.resolve(data);
106 | self.timestamp = self.timestamp + 1.0;
107 | false
108 | },
109 | GlWindowVRMessage::StopFrame(width, height, buffer) => {
110 | debug!("VR stop frame {}x{} ({})", width, height, buffer.len());
111 | // TODO: render the buffer contents
112 | let context = self.gl_context.take().expect("Context was current");
113 | let context = match unsafe { context.make_current() } {
114 | Err(err) => {
115 | error!("VR Display failed to make window current ({:?})", err);
116 | return true;
117 | },
118 | Ok(context) => context,
119 | };
120 | if self.texture_id == 0 {
121 | self.texture_id = self.gl.gen_textures(1)[0];
122 | debug!("Generated texture {}", self.texture_id);
123 | }
124 | if self.framebuffer_id == 0 {
125 | self.framebuffer_id = self.gl.gen_framebuffers(1)[0];
126 | debug!("Generated framebuffer {}", self.framebuffer_id);
127 | }
128 |
129 | self.gl.clear_color(0.2, 0.3, 0.3, 1.0);
130 | self.gl.clear(gl::COLOR_BUFFER_BIT);
131 |
132 | self.gl.bind_texture(gl::TEXTURE_2D, self.texture_id);
133 | self.gl.tex_image_2d(
134 | gl::TEXTURE_2D,
135 | 0,
136 | gl::RGBA as gl::GLint,
137 | width as gl::GLsizei,
138 | height as gl::GLsizei,
139 | 0,
140 | gl::RGBA,
141 | gl::UNSIGNED_BYTE,
142 | Some(&buffer[..]),
143 | );
144 | self.gl.bind_texture(gl::TEXTURE_2D, 0);
145 |
146 | self.gl.bind_framebuffer(gl::READ_FRAMEBUFFER, self.framebuffer_id);
147 | self.gl.framebuffer_texture_2d(
148 | gl::READ_FRAMEBUFFER,
149 | gl::COLOR_ATTACHMENT0,
150 | gl::TEXTURE_2D,
151 | self.texture_id,
152 | 0
153 | );
154 | self.gl.viewport(
155 | 0, 0, width as gl::GLsizei, height as gl::GLsizei,
156 | );
157 | self.gl.blit_framebuffer(
158 | 0, 0, width as gl::GLsizei, height as gl::GLsizei,
159 | 0, 0, width as gl::GLsizei, height as gl::GLsizei,
160 | gl::COLOR_BUFFER_BIT,
161 | gl::NEAREST,
162 | );
163 | self.gl.bind_framebuffer(gl::READ_FRAMEBUFFER, 0);
164 |
165 | let _ = context.swap_buffers();
166 |
167 | let err = self.gl.get_error();
168 | if err != 0 {
169 | error!("Test VR Display GL error {}.", err);
170 | }
171 |
172 | let context = match unsafe { context.make_not_current() } {
173 | Err(err) => {
174 | error!("VR Display failed to make window non current ({:?})", err);
175 | return true;
176 | },
177 | Ok(context) => context,
178 | };
179 |
180 | self.gl_context = Some(context);
181 |
182 | true
183 | },
184 | GlWindowVRMessage::StopPresenting => {
185 | debug!("VR stopping");
186 | self.gl_context.as_ref().unwrap().window().hide();
187 | self.presenting = false;
188 | true
189 | },
190 | }
191 | }
192 |
193 | fn handle_window_events(&mut self) {
194 | let view = &mut self.view;
195 | if let Some(ref mut events_loop) = self.events_loop {
196 | events_loop.poll_events(|event| {
197 | if let Event::WindowEvent { event: WindowEvent::KeyboardInput { input, .. }, .. } = event {
198 | if let Some(key_code) = input.virtual_keycode {
199 | let delta = match key_code {
200 | VirtualKeyCode::Up => RigidTransform3D::from_translation(Vector3D::new(0.0, 0.0, DELTA)),
201 | VirtualKeyCode::Down => RigidTransform3D::from_translation(Vector3D::new(0.0, 0.0, -DELTA)),
202 | VirtualKeyCode::Left => RigidTransform3D::from_translation(Vector3D::new(-DELTA, 0.0, 0.0)),
203 | VirtualKeyCode::Right => RigidTransform3D::from_translation(Vector3D::new(DELTA, 0.0, 0.0)),
204 | VirtualKeyCode::W => RigidTransform3D::from_rotation(Rotation3D::around_x(ANGLE)),
205 | VirtualKeyCode::S => RigidTransform3D::from_rotation(Rotation3D::around_x(-ANGLE)),
206 | VirtualKeyCode::A => RigidTransform3D::from_rotation(Rotation3D::around_y(ANGLE)),
207 | VirtualKeyCode::D => RigidTransform3D::from_rotation(Rotation3D::around_y(-ANGLE)),
208 | _ => RigidTransform3D::identity(),
209 | };
210 | *view = view.post_transform(&delta);
211 | }
212 | }
213 | })
214 | }
215 | }
216 | }
217 |
218 | pub(crate) enum GlWindowVRMessage {
219 | StartPresenting,
220 | StartFrame(f64, f64, VRResolveFrameData),
221 | StopFrame(u32, u32, Arc>),
222 | StopPresenting,
223 | }
224 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/magicleap/display.rs:
--------------------------------------------------------------------------------
1 | use rust_webvr_api::VRDisplay;
2 | use rust_webvr_api::VRDisplayData;
3 | use rust_webvr_api::VRFrameData;
4 | use rust_webvr_api::VRFutureFrameData;
5 | use rust_webvr_api::VRFramebuffer;
6 | use rust_webvr_api::VRFramebufferAttributes;
7 | use rust_webvr_api::VRGamepadPtr;
8 | use rust_webvr_api::VRLayer;
9 | use sparkle::gl;
10 | use sparkle::gl::Gl;
11 | use sparkle::gl::types::GLint;
12 | use sparkle::gl::types::GLuint;
13 | use std::cell::RefCell;
14 | use std::sync::Arc;
15 | use std::sync::mpsc::Sender;
16 | use super::heartbeat::MagicLeapVRMessage;
17 |
18 | pub type MagicLeapVRDisplayPtr = Arc>;
19 |
20 | pub struct MagicLeapVRDisplay {
21 | display_data: VRDisplayData,
22 | sender: Sender,
23 | fbos: Vec,
24 | texture_id_pool: ArcPool,
25 | }
26 |
27 | unsafe impl Sync for MagicLeapVRDisplay {}
28 |
29 | impl Drop for MagicLeapVRDisplay {
30 | fn drop(&mut self) {
31 | self.stop_present();
32 | }
33 | }
34 |
35 | impl VRDisplay for MagicLeapVRDisplay {
36 | fn id(&self) -> u32 {
37 | self.display_data.display_id
38 | }
39 |
40 | fn data(&self) -> VRDisplayData {
41 | self.display_data.clone()
42 | }
43 |
44 | fn immediate_frame_data(&self, _near: f64, _far: f64) -> VRFrameData {
45 | VRFrameData::default()
46 | }
47 |
48 | fn synced_frame_data(&self, _near: f64, _far: f64) -> VRFrameData {
49 | unimplemented!()
50 | }
51 |
52 | fn reset_pose(&mut self) {
53 | unimplemented!()
54 | }
55 |
56 | fn sync_poses(&mut self) {
57 | unimplemented!()
58 | }
59 |
60 | fn future_frame_data(&mut self, near: f64, far: f64) -> VRFutureFrameData {
61 | let (resolver, result) = VRFutureFrameData::blocked();
62 | let _ = self.sender.send(MagicLeapVRMessage::StartFrame(near as f32, far as f32, resolver));
63 | result
64 | }
65 |
66 | fn bind_framebuffer(&mut self, _eye_index: u32) {
67 | unimplemented!()
68 | }
69 |
70 | fn get_framebuffers(&self) -> Vec {
71 | unimplemented!()
72 | }
73 |
74 | fn render_layer(&mut self, _layer: &VRLayer) {
75 | unreachable!()
76 | }
77 |
78 | fn submit_frame(&mut self) {
79 | unreachable!()
80 | }
81 |
82 | fn submit_layer(&mut self, gl: &Gl, layer: &VRLayer) {
83 | // So... why does this blit exist? Well...
84 | // submit_layer is called from the WebGL thread,
85 | // but we need to display the texture in the main thread,
86 | // so we send it to the main thread for display.
87 | // the WebGL thread *cannot block* waiting on the main thread,
88 | // since this might introduce deadlock
89 | // https://github.com/servo/servo/issues/22914
90 | // so we have to return immediately.
91 | // Unfortunately, this means the WebGL thread may
92 | // then update the texture, and so we end up with the main
93 | // thread displaying a texture that the WebGL thread is in
94 | // the middle of updating, which produces flickering.
95 | // This is the same issue as https://github.com/servo/servo/issues/21838.
96 | // The trick we use to avoid this is to use a pool of GL textures,
97 | // and send the main thread an element from the pool.
98 | // We send it as an `PooledGLTextureId`, which uses an `Arc` under the hood,
99 | // so when the main thread is no longer using the texture, it gets returned
100 | // to the pool. It might be nice to use the same trick for webrender,
101 | // but that probably involves changing the webrender API.
102 | let pooled_id = self.blit_texture(gl, layer);
103 | let texture_id = pooled_id.texture_id();
104 | let layer = VRLayer { texture_id, ..layer.clone() };
105 | let _ = self.sender.send(MagicLeapVRMessage::StopFrame(layer, pooled_id));
106 | }
107 |
108 | fn start_present(&mut self, _attributes: Option) {
109 | let _ = self.sender.send(MagicLeapVRMessage::StartPresenting);
110 | }
111 |
112 | fn stop_present(&mut self) {
113 | let _ = self.sender.send(MagicLeapVRMessage::StopPresenting);
114 | }
115 |
116 | fn fetch_gamepads(&mut self) -> Result, String> {
117 | Ok(vec![])
118 | }
119 | }
120 |
121 | impl MagicLeapVRDisplay {
122 | pub(crate) fn new(
123 | display_data: VRDisplayData,
124 | sender: Sender
125 | ) -> MagicLeapVRDisplay {
126 | info!("Creating VRDisplay");
127 | let fbos = Vec::new();
128 | let texture_id_pool = ArcPool::new();
129 | MagicLeapVRDisplay { display_data, sender, fbos, texture_id_pool }
130 | }
131 |
132 | fn blit_texture(&mut self, gl: &Gl, layer: &VRLayer) -> PooledGLTextureId {
133 | // Sigh, all this code just to copy a texture...
134 |
135 | // The dimensions of the texture
136 | let (texture_w, texture_h) = layer
137 | .texture_size
138 | .map(|(w, h)| (w as GLint, h as GLint))
139 | .unwrap_or((2560, 960));
140 |
141 | // Save the current FBO bindings
142 | let mut current_fbos = [0, 0];
143 | unsafe { gl.get_integer_v(gl::DRAW_FRAMEBUFFER_BINDING, &mut current_fbos[0..]) };
144 | unsafe { gl.get_integer_v(gl::READ_FRAMEBUFFER_BINDING, &mut current_fbos[1..]) };
145 | debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
146 |
147 | // Set the FBOs to be ours
148 | if self.fbos.len() < 2 { self.fbos = gl.gen_framebuffers(2); }
149 | gl.bind_framebuffer(gl::READ_FRAMEBUFFER, self.fbos[0]);
150 | gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, self.fbos[1]);
151 | debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
152 |
153 | // Bind the source texture to the read FBO
154 | gl.framebuffer_texture_2d(gl::READ_FRAMEBUFFER,
155 | gl::COLOR_ATTACHMENT0,
156 | gl::TEXTURE_2D,
157 | layer.texture_id, 0);
158 | debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
159 |
160 | // Set the viewport
161 | gl.viewport(0, 0, texture_w, texture_h);
162 | debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
163 |
164 | // Get the destination texture from the pool, or create a new one
165 | let pooled_id = self.pooled_texture_id(gl, texture_w, texture_h);
166 | let texture_id = pooled_id.texture_id();
167 |
168 | // Bind the destination texture to the draw FBO
169 | gl.framebuffer_texture_2d(gl::DRAW_FRAMEBUFFER,
170 | gl::COLOR_ATTACHMENT0,
171 | gl::TEXTURE_2D,
172 | texture_id, 0);
173 | debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
174 |
175 | // Do the blit
176 | debug!("Blitting from {} to {} ({}x{})", layer.texture_id, texture_id, texture_w, texture_h);
177 | gl.blit_framebuffer(0, 0, texture_w, texture_h,
178 | 0, 0, texture_w, texture_h,
179 | gl::COLOR_BUFFER_BIT, gl::LINEAR);
180 | debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
181 |
182 | // Restore the old framebuffers
183 | gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, current_fbos[0] as GLuint);
184 | gl.bind_framebuffer(gl::READ_FRAMEBUFFER, current_fbos[1] as GLuint);
185 | debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
186 |
187 | // Flush commands so they're seen by the main thread
188 | gl.flush();
189 | debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
190 |
191 | // Done!
192 | pooled_id
193 | }
194 |
195 | fn pooled_texture_id(&mut self, gl: &Gl, width: GLint, height: GLint) -> PooledGLTextureId {
196 | let texture_id = self.texture_id_pool.remove().unwrap_or_else(|| {
197 | let texture_id = gl.gen_textures(1)[0];
198 | debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
199 |
200 | gl.bind_texture(gl::TEXTURE_2D, texture_id);
201 | debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
202 |
203 | gl.tex_image_2d(gl::TEXTURE_2D,
204 | 0,
205 | gl::RGBA as GLint,
206 | width, height,
207 | 0,
208 | gl::RGBA,
209 | gl::UNSIGNED_BYTE,
210 | None);
211 | debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
212 |
213 | gl.bind_texture(gl::TEXTURE_2D, 0);
214 | debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
215 |
216 | texture_id
217 | });
218 | PooledGLTextureId(self.texture_id_pool.add(texture_id))
219 | }
220 | }
221 |
222 | // A pool of Arc's.
223 | // You can add a T into the pool, and get back an Arc.
224 | // You can request a T from the pool, if there's an Arc with no other owners,
225 | // it will be removed from the pool, unwrapped and returned.
226 |
227 | struct ArcPool(Vec>);
228 |
229 | impl ArcPool {
230 | fn new() -> ArcPool {
231 | ArcPool(Vec::new())
232 | }
233 |
234 | fn add(&mut self, val: T) -> Arc {
235 | let result = Arc::new(val);
236 | self.0.push(result.clone());
237 | result
238 | }
239 |
240 | fn remove(&mut self) -> Option {
241 | let i = self.0.iter().position(|arc| Arc::strong_count(arc) == 1);
242 | i.and_then(|i| Arc::try_unwrap(self.0.swap_remove(i)).ok())
243 | }
244 | }
245 |
246 | // A pooled GLTextureId
247 |
248 | pub struct PooledGLTextureId(Arc);
249 |
250 | impl PooledGLTextureId {
251 | pub fn texture_id(&self) -> GLuint {
252 | *self.0
253 | }
254 | }
255 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/glwindow/display.rs:
--------------------------------------------------------------------------------
1 | use euclid::Angle;
2 | use euclid::default::RigidTransform3D;
3 | use euclid::Trig;
4 | use euclid::Vector3D;
5 | use rust_webvr_api::utils;
6 | use rust_webvr_api::VRDisplay;
7 | use rust_webvr_api::VRDisplayCapabilities;
8 | use rust_webvr_api::VRDisplayData;
9 | use rust_webvr_api::VREyeParameters;
10 | use rust_webvr_api::VRFieldOfView;
11 | use rust_webvr_api::VRFrameData;
12 | use rust_webvr_api::VRFutureFrameData;
13 | use rust_webvr_api::VRFramebuffer;
14 | use rust_webvr_api::VRFramebufferAttributes;
15 | use rust_webvr_api::VRGamepadPtr;
16 | use rust_webvr_api::VRLayer;
17 | use rust_webvr_api::VRViewport;
18 | use sparkle::gl;
19 | use sparkle::gl::Gl;
20 | use std::cell::RefCell;
21 | use std::sync::Arc;
22 | use std::sync::mpsc::Sender;
23 | use super::heartbeat::GlWindowVRMessage;
24 | use glutin::dpi::PhysicalSize;
25 |
26 | // Fake a display with a distance between eyes of 5cm.
27 | const EYE_DISTANCE: f32 = 0.05;
28 |
29 | pub type GlWindowVRDisplayPtr = Arc>;
30 |
31 | pub struct GlWindowVRDisplay {
32 | id: u32,
33 | name: String,
34 | size: PhysicalSize,
35 | sender: Sender,
36 | pool: ArcPool>,
37 | }
38 |
39 | unsafe impl Sync for GlWindowVRDisplay {}
40 |
41 | impl Drop for GlWindowVRDisplay {
42 | fn drop(&mut self) {
43 | self.stop_present();
44 | }
45 | }
46 |
47 | impl VRDisplay for GlWindowVRDisplay {
48 | fn id(&self) -> u32 {
49 | self.id
50 | }
51 |
52 | fn data(&self) -> VRDisplayData {
53 | let capabilities = VRDisplayCapabilities {
54 | has_position: false,
55 | has_orientation: false,
56 | has_external_display: true,
57 | can_present: true,
58 | presented_by_browser: false,
59 | max_layers: 1,
60 | };
61 |
62 | let fov_right = GlWindowVRDisplay::fov_right(self.size).to_degrees();
63 | let fov_up = GlWindowVRDisplay::fov_up(self.size).to_degrees();
64 |
65 | let field_of_view = VRFieldOfView {
66 | down_degrees: fov_up,
67 | left_degrees: fov_right,
68 | right_degrees: fov_right,
69 | up_degrees: fov_up,
70 | };
71 |
72 | let left_eye_parameters = VREyeParameters {
73 | offset: [-EYE_DISTANCE / 2.0, 0.0, 0.0],
74 | render_width: self.size.width as u32 / 2,
75 | render_height: self.size.height as u32,
76 | field_of_view: field_of_view,
77 | };
78 |
79 | let right_eye_parameters = VREyeParameters {
80 | offset: [EYE_DISTANCE / 2.0, 0.0, 0.0],
81 | ..left_eye_parameters.clone()
82 | };
83 |
84 | VRDisplayData {
85 | display_id: self.id,
86 | display_name: self.name.clone(),
87 | connected: true,
88 | capabilities: capabilities,
89 | stage_parameters: None,
90 | left_eye_parameters: left_eye_parameters,
91 | right_eye_parameters: right_eye_parameters,
92 | }
93 | }
94 |
95 | fn immediate_frame_data(&self, near: f64, far: f64) -> VRFrameData {
96 | GlWindowVRDisplay::frame_data(0.0, self.size, near, far, RigidTransform3D::identity())
97 | }
98 |
99 | fn synced_frame_data(&self, near: f64, far: f64) -> VRFrameData {
100 | self.immediate_frame_data(near, far)
101 | }
102 |
103 | fn reset_pose(&mut self) {}
104 |
105 | fn sync_poses(&mut self) {}
106 |
107 | fn future_frame_data(&mut self, near: f64, far: f64) -> VRFutureFrameData {
108 | let (resolver, result) = VRFutureFrameData::blocked();
109 | let _ = self.sender.send(GlWindowVRMessage::StartFrame(near, far, resolver));
110 | result
111 | }
112 |
113 | fn bind_framebuffer(&mut self, _eye_index: u32) {}
114 |
115 | fn get_framebuffers(&self) -> Vec {
116 | let left_viewport = VRViewport {
117 | x: 0,
118 | y: 0,
119 | width: (self.size.width as i32) / 2,
120 | height: self.size.height as i32,
121 | };
122 |
123 | let right_viewport = VRViewport {
124 | x: self.size.width as i32 - left_viewport.width,
125 | ..left_viewport
126 | };
127 |
128 | vec![
129 | VRFramebuffer {
130 | eye_index: 0,
131 | attributes: VRFramebufferAttributes::default(),
132 | viewport: left_viewport,
133 | },
134 | VRFramebuffer {
135 | eye_index: 1,
136 | attributes: VRFramebufferAttributes::default(),
137 | viewport: right_viewport,
138 | },
139 | ]
140 | }
141 |
142 | fn render_layer(&mut self, _layer: &VRLayer) {
143 | unreachable!()
144 | }
145 |
146 | fn submit_frame(&mut self) {
147 | unreachable!()
148 | }
149 |
150 | fn submit_layer(&mut self, gl: &Gl, layer: &VRLayer) {
151 | // TODO: this assumes that the current GL framebuffer contains the texture
152 | // TODO: what to do if the layer has no texture_size?
153 | if let Some((width, height)) = layer.texture_size {
154 | let num_bytes = (width as usize) * (height as usize) * 4;
155 | let mut buffer = self.pool.remove().unwrap_or_else(Vec::new);
156 | buffer.resize(num_bytes, 0);
157 | gl.read_pixels_into_buffer(
158 | 0,
159 | 0,
160 | width as gl::GLsizei,
161 | height as gl::GLsizei,
162 | gl::RGBA,
163 | gl::UNSIGNED_BYTE,
164 | &mut buffer[..],
165 | );
166 | let buffer = self.pool.add(buffer);
167 | let _ = self.sender.send(GlWindowVRMessage::StopFrame(width, height, buffer));
168 | }
169 | }
170 |
171 | fn start_present(&mut self, _attributes: Option) {
172 | let _ = self.sender.send(GlWindowVRMessage::StartPresenting);
173 | }
174 |
175 | fn stop_present(&mut self) {
176 | let _ = self.sender.send(GlWindowVRMessage::StopPresenting);
177 | }
178 |
179 | fn fetch_gamepads(&mut self) -> Result, String> {
180 | Ok(vec![])
181 | }
182 | }
183 |
184 | impl GlWindowVRDisplay {
185 | pub(crate) fn new(
186 | name: String,
187 | size: PhysicalSize,
188 | sender: Sender
189 | ) -> GlWindowVRDisplay {
190 | GlWindowVRDisplay {
191 | id: utils::new_id(),
192 | name: name,
193 | size: size,
194 | sender: sender,
195 | pool: ArcPool::new(),
196 | }
197 | }
198 |
199 | fn fov_up(size: PhysicalSize) -> Angle {
200 | Angle::radians(f64::fast_atan2(
201 | 2.0 * size.height as f64,
202 | size.width as f64,
203 | ))
204 | }
205 |
206 | fn fov_right(size: PhysicalSize) -> Angle {
207 | Angle::radians(f64::fast_atan2(
208 | 2.0 * size.width as f64,
209 | size.height as f64,
210 | ))
211 | }
212 |
213 | fn perspective(size: PhysicalSize, near: f64, far: f64) -> [f32; 16] {
214 | // https://github.com/toji/gl-matrix/blob/bd3307196563fbb331b40fc6ebecbbfcc2a4722c/src/mat4.js#L1271
215 | let near = near as f32;
216 | let far = far as f32;
217 | let f = 1.0 / GlWindowVRDisplay::fov_up(size).radians.tan() as f32;
218 | let nf = 1.0 / (near - far);
219 | let aspect = ((size.width / 2.0) as f32) / (size.height as f32);
220 |
221 | // Dear rustfmt, This is a 4x4 matrix, please leave it alone. Best, ajeffrey.
222 | {#[rustfmt::skip]
223 | return [
224 | f / aspect, 0.0, 0.0, 0.0,
225 | 0.0, f, 0.0, 0.0,
226 | 0.0, 0.0, (far + near) * nf, -1.0,
227 | 0.0, 0.0, 2.0 * far * near * nf, 0.0,
228 | ];
229 | }
230 | }
231 |
232 | pub(crate) fn frame_data(timestamp: f64, size: PhysicalSize, near: f64, far: f64, view: RigidTransform3D) -> VRFrameData {
233 | let left_projection_matrix = GlWindowVRDisplay::perspective(size, near, far);
234 | let right_projection_matrix = left_projection_matrix.clone();
235 |
236 | let left_offset = RigidTransform3D::from_translation(Vector3D::new(EYE_DISTANCE / 2.0, 0.0, 0.0));
237 | let right_offset = RigidTransform3D::from_translation(Vector3D::new(-EYE_DISTANCE / 2.0, 0.0, 0.0));
238 |
239 | let left_view_matrix = view
240 | .post_transform(&left_offset)
241 | .to_transform()
242 | .to_row_major_array();
243 |
244 | let right_view_matrix = view
245 | .post_transform(&right_offset)
246 | .to_transform()
247 | .to_row_major_array();
248 |
249 | VRFrameData {
250 | timestamp,
251 | left_projection_matrix,
252 | right_projection_matrix,
253 | left_view_matrix,
254 | right_view_matrix,
255 | ..VRFrameData::default()
256 | }
257 | }
258 | }
259 |
260 | // A pool of Arc's.
261 | // You can add a T into the pool, and get back an Arc.
262 | // You can request a T from the pool, if there's an Arc with no other owners,
263 | // it will be removed from the pool, unwrapped and returned.
264 |
265 | struct ArcPool(Vec>);
266 |
267 | impl ArcPool {
268 | fn new() -> ArcPool {
269 | ArcPool(Vec::new())
270 | }
271 |
272 | fn add(&mut self, val: T) -> Arc {
273 | let result = Arc::new(val);
274 | self.0.push(result.clone());
275 | result
276 | }
277 |
278 | fn remove(&mut self) -> Option {
279 | let i = self.0.iter().position(|arc| Arc::strong_count(arc) == 1);
280 | i.and_then(|i| Arc::try_unwrap(self.0.swap_remove(i)).ok())
281 | }
282 | }
283 |
--------------------------------------------------------------------------------
/rust-webvr/src/api/openvr/service.rs:
--------------------------------------------------------------------------------
1 | use super::binding as openvr;
2 | use super::binding::EVRInitError::*;
3 | use super::binding::EVRApplicationType::*;
4 | use super::binding::ETrackedDeviceClass::*;
5 | use super::binding::EVREventType::*;
6 | use super::constants;
7 | use super::display::{OpenVRDisplay, OpenVRDisplayPtr};
8 | use super::gamepad::{OpenVRGamepad, OpenVRGamepadPtr};
9 | use super::library::OpenVRLibrary;
10 | use std::ffi::CString;
11 | use std::ptr;
12 | use std::mem;
13 | use {VRService, VRDisplay, VRDisplayPtr, VREvent, VRDisplayEvent, VRDisplayEventReason,
14 | VRGamepadEvent, VRGamepad, VRGamepadPtr};
15 |
16 | // OpenVR Service implementation
17 | pub struct OpenVRService {
18 | initialized: bool,
19 | lib: Option,
20 | displays: Vec,
21 | gamepads: Vec,
22 | system: *mut openvr::VR_IVRSystem_FnTable,
23 | chaperone: *mut openvr::VR_IVRChaperone_FnTable,
24 | }
25 |
26 | unsafe impl Send for OpenVRService {}
27 |
28 | impl VRService for OpenVRService {
29 | fn initialize(&mut self) -> Result<(), String> {
30 | if self.initialized {
31 | return Ok(());
32 | }
33 |
34 | // Load OpenVR library
35 | match unsafe { OpenVRLibrary::new() } {
36 | Ok(lib) => self.lib = Some(lib),
37 | Err(msg) => {
38 | return Err(format!("Error loading OpenVR dll: {:?}", msg));
39 | }
40 | };
41 |
42 | if !self.is_available() {
43 | return Err("Not available".into());
44 | }
45 |
46 | // Initialize OpenVR
47 | let mut error = EVRInitError_VRInitError_None;
48 | unsafe {
49 | (*self.lib.as_ref().unwrap().init_internal)(&mut error, EVRApplicationType_VRApplication_Scene);
50 | }
51 |
52 | if error as u32 != EVRInitError_VRInitError_None as u32 {
53 | return Err(format!("OpenVR Internal failed with error {}", error as u32));
54 | }
55 |
56 | // Initialize System
57 | error = EVRInitError_VRInitError_None;
58 | unsafe {
59 | let name = CString::new(format!("FnTable:{}", constants::IVRSystem_Version)).unwrap();
60 | self.system = (*self.lib.as_ref().unwrap().get_interface)(name.as_ptr(), &mut error)
61 | as *mut openvr::VR_IVRSystem_FnTable;
62 | }
63 |
64 | if error as u32 != EVRInitError_VRInitError_None as u32 {
65 | return Err(format!("OpenVR GetGenericInterface failed with error {}", error as u32));
66 | }
67 |
68 | // Initialize Chaperone
69 | error = EVRInitError_VRInitError_None;
70 | unsafe {
71 | let name = CString::new(format!("FnTable:{}", constants::IVRChaperone_Version)).unwrap();
72 | self.chaperone = (*self.lib.as_ref().unwrap().get_interface)(name.as_ptr(), &mut error)
73 | as *mut openvr::VR_IVRChaperone_FnTable;
74 | }
75 |
76 | if error as u32 != EVRInitError_VRInitError_None as u32 {
77 | return Err(format!("OpenVR GetGenericInterface failed with error {:?}", error));
78 | }
79 |
80 | self.initialized = true;
81 | Ok(())
82 | }
83 |
84 | fn fetch_displays(&mut self) -> Result,String> {
85 | self.init_displays()?;
86 |
87 | Ok(self.displays.iter().map(|d| d.clone() as VRDisplayPtr).collect())
88 | }
89 |
90 | fn fetch_gamepads(&mut self) -> Result,String> {
91 | self.init_displays()?;
92 |
93 | Ok(self.gamepads.iter().map(|d| d.clone() as VRGamepadPtr).collect())
94 | }
95 |
96 | fn is_available(&self) -> bool {
97 | unsafe {
98 | match self.lib {
99 | Some(ref lib) => (*lib.is_hmd_present)(),
100 | None => false
101 | }
102 | }
103 | }
104 |
105 | fn poll_events(&self) -> Vec {
106 | let mut result = Vec::new();
107 | if !self.initialized || self.system.is_null() {
108 | return result;
109 | }
110 | let mut event: openvr::VREvent_t = unsafe { mem::uninitialized() };
111 | let size = mem::size_of::() as u32;
112 | while unsafe { (*self.system).PollNextEvent.unwrap()(&mut event, size) } {
113 |
114 | let event_type: openvr::EVREventType = unsafe { mem::transmute(event.eventType) };
115 |
116 | match event_type {
117 | EVREventType_VREvent_TrackedDeviceUserInteractionStarted => {
118 | if let Some(display) = self.get_display(event.trackedDeviceIndex) {
119 | result.push(VRDisplayEvent::Activate(display.borrow().data(),
120 | VRDisplayEventReason::Mounted)
121 | .into());
122 | }
123 | },
124 | EVREventType_VREvent_TrackedDeviceUserInteractionEnded => {
125 | if let Some(display) = self.get_display(event.trackedDeviceIndex) {
126 | result.push(VRDisplayEvent::Deactivate(display.borrow().data(),
127 | VRDisplayEventReason::Unmounted)
128 | .into());
129 | }
130 | },
131 | EVREventType_VREvent_TrackedDeviceActivated => {
132 | if let Some(display) = self.get_display(event.trackedDeviceIndex) {
133 | result.push(VRDisplayEvent::Connect(display.borrow().data()).into())
134 | }
135 | else if let Some(gamepad) = self.get_gamepad(event.trackedDeviceIndex) {
136 | let g = gamepad.borrow();
137 | result.push(VRGamepadEvent::Connect(g.data(), g.state()).into());
138 | }
139 | },
140 | EVREventType_VREvent_TrackedDeviceDeactivated => {
141 | if let Some(display) = self.get_display(event.trackedDeviceIndex) {
142 | result.push(VRDisplayEvent::Disconnect(display.borrow().id()).into())
143 | }
144 | else if let Some(gamepad) = self.get_gamepad(event.trackedDeviceIndex) {
145 | result.push(VRGamepadEvent::Disconnect(gamepad.borrow().id()).into());
146 | }
147 | },
148 | EVREventType_VREvent_DashboardActivated => {
149 | if let Some(display) = self.get_display(event.trackedDeviceIndex) {
150 | result.push(VRDisplayEvent::Blur(display.borrow().data()).into())
151 | }
152 | },
153 | EVREventType_VREvent_DashboardDeactivated => {
154 | if let Some(display) = self.get_display(event.trackedDeviceIndex) {
155 | result.push(VRDisplayEvent::Focus(display.borrow().data()).into())
156 | }
157 | },
158 | EVREventType_VREvent_ChaperoneDataHasChanged |
159 | EVREventType_VREvent_IpdChanged |
160 | EVREventType_VREvent_TrackedDeviceUpdated => {
161 | if let Some(display) = self.get_display(event.trackedDeviceIndex) {
162 | result.push(VRDisplayEvent::Change(display.borrow().data()).into())
163 | }
164 | },
165 | _ => {}
166 | };
167 | }
168 |
169 | result
170 | }
171 | }
172 |
173 | impl Drop for OpenVRService {
174 | fn drop(&mut self) {
175 | if self.initialized {
176 | unsafe {
177 | self.gamepads.clear();
178 | self.displays.clear();
179 | println!("OpenVR Shutdown");
180 | (*self.lib.as_ref().unwrap().shutdown_internal)();
181 | }
182 | }
183 | }
184 | }
185 |
186 | impl OpenVRService {
187 | pub fn new() -> OpenVRService {
188 | OpenVRService {
189 | initialized: false,
190 | lib: None,
191 | displays: Vec::new(),
192 | gamepads: Vec::new(),
193 | system: ptr::null_mut(),
194 | chaperone: ptr::null_mut()
195 | }
196 | }
197 |
198 | fn init_displays(&mut self) -> Result<(), String> {
199 | // Return cached displays if available
200 | if self.initialized && self.displays.len() > 0 {
201 | return Ok(());
202 | }
203 | // Ensure that there are not initialization errors
204 | self.initialize()?;
205 |
206 | let max_device_count: u32 = openvr::k_unMaxTrackedDeviceCount;
207 | self.displays.clear();
208 | let mut gamepad_ids = vec![];
209 | for i in 0..max_device_count {
210 | let device_class: openvr::ETrackedDeviceClass = unsafe {
211 | (*self.system).GetTrackedDeviceClass.unwrap()(i as openvr::TrackedDeviceIndex_t)
212 | };
213 |
214 | match device_class {
215 | ETrackedDeviceClass_TrackedDeviceClass_HMD => {
216 | self.displays.push(OpenVRDisplay::new(self.lib.as_ref().unwrap(), i, self.system, self.chaperone));
217 | },
218 | ETrackedDeviceClass_TrackedDeviceClass_Controller => {
219 | gamepad_ids.push(i);
220 | }
221 | _ => ()
222 | }
223 | }
224 |
225 | let display_id = if let Some(ref d) = self.displays.first() {
226 | d.borrow().id()
227 | } else {
228 | 0
229 | };
230 |
231 |
232 | for id in gamepad_ids {
233 | self.gamepads.push(OpenVRGamepad::new(id, self.system, display_id));
234 | }
235 |
236 | if let Some(ref d) = self.displays.first() {
237 | d.borrow_mut().set_gamepads(self.gamepads.clone());
238 | }
239 | Ok(())
240 | }
241 |
242 | pub fn get_display(&self, index: openvr::TrackedDeviceIndex_t) -> Option<&OpenVRDisplayPtr> {
243 | self.displays.iter().find(|&d| d.borrow().index() == index)
244 | }
245 |
246 | pub fn get_gamepad(&self, index: openvr::TrackedDeviceIndex_t) -> Option<&OpenVRGamepadPtr> {
247 | self.gamepads.iter().find(|&d| d.borrow().index() == index)
248 | }
249 | }
--------------------------------------------------------------------------------
/rust-webvr/src/api/oculusvr/gamepad.rs:
--------------------------------------------------------------------------------
1 | #![cfg(target_os="android")]
2 | #![cfg(feature = "oculusvr")]
3 |
4 | use {VRGamepad, VRGamepadButton, VRGamepadData, VRGamepadHand, VRGamepadState};
5 | use ovr_mobile_sys as ovr;
6 | use ovr_mobile_sys::ovrButton::*;
7 | use ovr_mobile_sys::ovrControllerCapabilties::*;
8 | use ovr_mobile_sys::ovrControllerType::*;
9 | use std::cell::{Cell, RefCell};
10 | use std::mem;
11 | use std::ptr;
12 | use std::sync::Arc;
13 | use super::display::{ovr_quat_to_array, ovr_vec3_to_array};
14 | use rust_webvr_api::utils;
15 |
16 | pub type OculusVRGamepadPtr = Arc>;
17 |
18 | pub struct OculusVRGamepad {
19 | ovr: *mut ovr::ovrMobile,
20 | ovr_id: ovr::ovrDeviceID,
21 | ovr_type: ovr::ovrControllerType,
22 | connected: bool,
23 | capabilities: InputCapabilities,
24 | gamepad_id: u32,
25 | display_id: u32,
26 | predicted_display_time: Cell,
27 | }
28 |
29 | unsafe impl Send for OculusVRGamepad {}
30 | unsafe impl Sync for OculusVRGamepad {}
31 |
32 | impl OculusVRGamepad {
33 | pub fn new(ovr: *mut ovr::ovrMobile,
34 | ovr_id: ovr::ovrDeviceID,
35 | ovr_type: ovr::ovrControllerType,
36 | display_id: u32)
37 | -> Arc>
38 | {
39 | let capabilities = InputCapabilities::from_ovr(ovr, ovr_type, ovr_id);
40 |
41 | let gamepad = Self {
42 | ovr: ovr,
43 | ovr_id: ovr_id,
44 | ovr_type: ovr_type,
45 | connected: true,
46 | capabilities: capabilities.unwrap_or_default(),
47 | gamepad_id: utils::new_id(),
48 | display_id: display_id,
49 | predicted_display_time: Cell::new(0.0),
50 | };
51 |
52 | Arc::new(RefCell::new(gamepad))
53 | }
54 |
55 | pub fn refresh_available_gamepads(ovr: *mut ovr::ovrMobile,
56 | display_id: u32,
57 | out: &mut Vec) {
58 | let mut index = 0;
59 | // Reset connected status
60 | for gamepad in out.iter() {
61 | gamepad.borrow_mut().connected = false;
62 | }
63 |
64 | loop {
65 | let mut caps: ovr::ovrInputCapabilityHeader = unsafe { mem::uninitialized() };
66 |
67 | if unsafe { ovr::vrapi_EnumerateInputDevices(ovr, index, &mut caps) } < 0 {
68 | // No more input devices to enumerate
69 | break;
70 | }
71 |
72 | index += 1;
73 |
74 | if caps.Type != ovrControllerType_TrackedRemote && caps.Type != ovrControllerType_Headset {
75 | // Not interested in this kind of input device
76 | continue;
77 | }
78 |
79 | // Update if the controller type already exists
80 | if let Some(gamepad) = out.iter().find(|g| g.borrow().ovr_type == caps.Type).as_ref() {
81 | let mut gamepad = gamepad.borrow_mut();
82 | gamepad.ovr = ovr;
83 | gamepad.ovr_id = caps.DeviceID;
84 | gamepad.connected = true;
85 | gamepad.update_capabilities();
86 | continue;
87 | }
88 |
89 | // Create new Gamepad instance
90 | let gamepad = OculusVRGamepad::new(ovr, caps.DeviceID, caps.Type, display_id);
91 | out.push(gamepad);
92 | }
93 | }
94 |
95 | pub fn update_capabilities(&mut self) {
96 | if let Ok(capabilities) = InputCapabilities::from_ovr(self.ovr, self.ovr_type, self.ovr_id) {
97 | self.capabilities = capabilities;
98 | }
99 | }
100 |
101 | // Sensor input is only available while in VR mode.
102 | pub fn on_exit_vrmode(&mut self) {
103 | self.connected = false;
104 | self.ovr = ptr::null_mut();
105 | }
106 |
107 | fn fetch_axes(&self, touching: bool, pos: &ovr::ovrVector2f, out: &mut VRGamepadState) {
108 | // Axes
109 | // Touchpad: (0,0) is the top-left corner.
110 | // Map to -1 1 for each axis.
111 | let x = pos.x / self.capabilities.trackpad_max_x as f32;
112 | let y = pos.y / self.capabilities.trackpad_max_y as f32;
113 | out.axes = if touching {
114 | [x as f64 * 2.0 - 1.0,
115 | y as f64 * 2.0 - 1.0].to_vec()
116 | } else {
117 | [0.0, 0.0].to_vec()
118 | };
119 | }
120 |
121 | fn fetch_remote_controller_state(&self, out: &mut VRGamepadState) {
122 | let mut state: ovr::ovrInputStateTrackedRemote = unsafe { mem::zeroed() };
123 | state.Header.ControllerType = ovrControllerType_TrackedRemote;
124 | unsafe {
125 | ovr::vrapi_GetCurrentInputState(self.ovr, self.ovr_id, &mut state.Header);
126 | }
127 | let touching_trackpad = state.TrackpadStatus > 0;
128 |
129 | // Axes
130 | self.fetch_axes(touching_trackpad, &state.TrackpadPosition, out);
131 |
132 | // 0 - Trackpad
133 | out.buttons.push(VRGamepadButton::new(touching_trackpad));
134 |
135 | // 1 - Trigger A
136 | out.buttons.push(VRGamepadButton::new(state.Buttons & (ovrButton_A as u32) > 0));
137 | }
138 |
139 | fn fetch_headset_controller_state(&self, out: &mut VRGamepadState) {
140 | let mut state: ovr::ovrInputStateHeadset = unsafe { mem::zeroed() };
141 | state.Header.ControllerType = ovrControllerType_Headset;
142 | unsafe {
143 | ovr::vrapi_GetCurrentInputState(self.ovr, self.ovr_id, &mut state.Header);
144 | }
145 | let touching_trackpad = state.TrackpadStatus > 0;
146 |
147 | // Axes
148 | self.fetch_axes(touching_trackpad, &state.TrackpadPosition, out);
149 |
150 | // 0 - Trackpad
151 | out.buttons.push(VRGamepadButton::new(touching_trackpad));
152 |
153 | // 1 - Trigger A
154 | out.buttons.push(VRGamepadButton::new(state.Buttons & (ovrButton_A as u32) > 0));
155 | }
156 |
157 | fn fetch_tracking_state(&self, out: &mut VRGamepadState) {
158 | let mut tracking: ovr::ovrTracking = unsafe { mem::uninitialized() };
159 | let status = unsafe {
160 | ovr::vrapi_GetInputTrackingState(self.ovr,
161 | self.ovr_id,
162 | self.predicted_display_time.get(),
163 | &mut tracking)
164 | };
165 |
166 | if status != ovr::ovrSuccessResult::ovrSuccess as i32 {
167 | out.connected = false;
168 | return;
169 | }
170 |
171 | if self.capabilities.controller_capabilities & (ovrControllerCaps_HasOrientationTracking as u32) > 0 {
172 | out.pose.orientation = Some(ovr_quat_to_array(&tracking.HeadPose.Pose.Orientation));
173 | }
174 |
175 | if self.capabilities.controller_capabilities & (ovrControllerCaps_HasPositionTracking as u32) > 0 {
176 | out.pose.position = Some(ovr_vec3_to_array(&tracking.HeadPose.Pose.Position));
177 | }
178 | }
179 |
180 | pub fn set_predicted_display_time(&self, time: f64) {
181 | self.predicted_display_time.set(time);
182 | }
183 | }
184 |
185 | impl VRGamepad for OculusVRGamepad {
186 | fn id(&self) -> u32 {
187 | self.gamepad_id
188 | }
189 |
190 | fn data(&self) -> VRGamepadData {
191 | let name = if self.ovr_type == ovrControllerType_TrackedRemote {
192 | "Gear VR Remote Controller"
193 | } else {
194 | "Gear VR Headset Controller"
195 | };
196 |
197 | let hand = if self.capabilities.controller_capabilities & (ovrControllerCaps_RightHand as u32) > 0 {
198 | VRGamepadHand::Right
199 | } else if self.capabilities.controller_capabilities & (ovrControllerCaps_LeftHand as u32) > 0 {
200 | VRGamepadHand::Left
201 | } else {
202 | VRGamepadHand::Unknown
203 | };
204 |
205 | VRGamepadData {
206 | display_id: self.display_id,
207 | name: name.into(),
208 | hand: hand,
209 | }
210 | }
211 |
212 | fn state(&self) -> VRGamepadState {
213 | let mut out = VRGamepadState::default();
214 |
215 | out.gamepad_id = self.gamepad_id;
216 | out.connected = self.connected && !self.ovr.is_null();
217 |
218 | if out.connected {
219 | if self.ovr_type == ovrControllerType_TrackedRemote {
220 | self.fetch_remote_controller_state(&mut out);
221 | } else {
222 | self.fetch_headset_controller_state(&mut out);
223 | }
224 | self.fetch_tracking_state(&mut out);
225 | }
226 |
227 | out
228 | }
229 | }
230 |
231 | struct InputCapabilities {
232 | controller_capabilities: u32,
233 | trackpad_max_x: u16,
234 | trackpad_max_y: u16,
235 | }
236 |
237 | impl InputCapabilities {
238 | pub fn from_ovr(ovr: *mut ovr::ovrMobile,
239 | ovr_type: ovr::ovrControllerType,
240 | ovr_id: ovr::ovrDeviceID) -> Result {
241 | if ovr_type == ovrControllerType_TrackedRemote {
242 | let mut caps: ovr::ovrInputTrackedRemoteCapabilities = unsafe { mem::uninitialized() };
243 | caps.Header.DeviceID = ovr_id;
244 | caps.Header.Type = ovr_type;
245 |
246 | let status = unsafe {
247 | ovr::vrapi_GetInputDeviceCapabilities(ovr, &mut caps.Header)
248 | };
249 |
250 | if status != ovr::ovrSuccessResult::ovrSuccess as i32 {
251 | return Err(());
252 | }
253 |
254 | Ok(Self {
255 | controller_capabilities: caps.ControllerCapabilities,
256 | trackpad_max_x: caps.TrackpadMaxX,
257 | trackpad_max_y: caps.TrackpadMaxY,
258 | })
259 |
260 | } else {
261 | let mut caps: ovr::ovrInputHeadsetCapabilities = unsafe { mem::uninitialized() };
262 | caps.Header.DeviceID = ovr_id;
263 | caps.Header.Type = ovr_type;
264 |
265 | let status = unsafe {
266 | ovr::vrapi_GetInputDeviceCapabilities(ovr, &mut caps.Header)
267 | };
268 |
269 | if status != ovr::ovrSuccessResult::ovrSuccess as i32 {
270 | return Err(());
271 | }
272 |
273 | Ok(Self {
274 | controller_capabilities: caps.ControllerCapabilities,
275 | trackpad_max_x: caps.TrackpadMaxX,
276 | trackpad_max_y: caps.TrackpadMaxY,
277 | })
278 | }
279 | }
280 | }
281 |
282 | impl Default for InputCapabilities {
283 | fn default() -> InputCapabilities {
284 | InputCapabilities {
285 | controller_capabilities: 0,
286 | trackpad_max_x: 299,
287 | trackpad_max_y: 199,
288 | }
289 | }
290 | }
291 |
--------------------------------------------------------------------------------