├── 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 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 | --------------------------------------------------------------------------------