├── .github └── workflows │ └── test.yml ├── .gitignore ├── .vscode └── settings.json ├── Cargo.toml ├── Makefile ├── README.md ├── assets └── textures │ └── uv-small.png ├── build.rs ├── docs ├── architecture.md ├── graphs.md ├── graphs2.md ├── graphs3.md ├── install_oculus.md ├── screenshot.webp ├── screenshot0.png ├── screenshot1.png ├── screenshot2.png └── screenshot3.png ├── examples ├── non_xr_scene.rs ├── xr_apk_scene.rs └── xr_pc_scene.rs ├── libs └── arm64-v8a │ ├── .gitignore │ └── .gitkeep ├── scripts ├── install_dependencies.bat └── update_dependencies.bat └── src ├── example_scene_plugin.rs └── lib.rs /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | test-linux: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Install dependencies 14 | run: sudo add-apt-repository ppa:monado-xr/monado && sudo apt-get update && sudo apt-get install make openjdk-11-jre g++ libudev-dev libasound2-dev gcc libopenxr-loader1 15 | 16 | - uses: actions/checkout@v2 17 | 18 | - name: Cache Cargo registry 19 | uses: actions/cache@v2 20 | with: 21 | path: ~/.cargo/registry 22 | key: ${{ matrix.build }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} 23 | restore-keys: | 24 | ${{ matrix.build }}-cargo-registry- 25 | 26 | - name: Cache Cargo index 27 | uses: actions/cache@v2 28 | with: 29 | path: ~/.cargo/git 30 | key: ${{ matrix.build }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} 31 | restore-keys: | 32 | ${{ matrix.build }}-cargo-index- 33 | 34 | - name: Cache Cargo build 35 | uses: actions/cache@v2 36 | with: 37 | path: target 38 | key: ${{ matrix.build }}-target-${{ hashFiles('**/Cargo.lock') }} 39 | restore-keys: | 40 | ${{ matrix.build }}-target- 41 | 42 | - name: Download dependencies 43 | run: make download_dependencies 44 | 45 | - name: Run tests 46 | run: cargo test 47 | 48 | test-windows: 49 | runs-on: windows-latest 50 | 51 | steps: 52 | - uses: actions/checkout@v2 53 | 54 | - name: Cache Cargo registry 55 | uses: actions/cache@v2 56 | with: 57 | path: ~/.cargo/registry 58 | key: ${{ matrix.build }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} 59 | restore-keys: | 60 | ${{ matrix.build }}-cargo-registry- 61 | 62 | - name: Cache Cargo index 63 | uses: actions/cache@v2 64 | with: 65 | path: ~/.cargo/git 66 | key: ${{ matrix.build }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} 67 | restore-keys: | 68 | ${{ matrix.build }}-cargo-index- 69 | 70 | - name: Cache Cargo build 71 | uses: actions/cache@v2 72 | with: 73 | path: target 74 | key: ${{ matrix.build }}-target-${{ hashFiles('**/Cargo.lock') }} 75 | restore-keys: | 76 | ${{ matrix.build }}-target- 77 | 78 | - name: Download dependencies 79 | run: .\scripts\install_dependencies.bat 80 | 81 | - name: Run tests 82 | run: cargo test 83 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /apk_target 3 | *.sw* 4 | /repos 5 | Cargo.lock 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "search.useGlobalIgnoreFiles": true 3 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xrbevy" 3 | version = "0.1.0" 4 | authors = ["Mika Vatanen "] 5 | edition = "2018" 6 | 7 | [features] 8 | default = [] 9 | 10 | [dependencies] 11 | bevy_openxr = { path = "./repos/bevy_openxr/crates/bevy_openxr" } 12 | bevy_openxr_core = { path = "./repos/bevy_openxr/crates/bevy_openxr_core" } 13 | 14 | [dependencies.bevy] 15 | path = "./repos/bevy" 16 | features = [ 17 | "bevy_dynamic_plugin", 18 | "bevy_gilrs", 19 | "bevy_gltf", 20 | "bevy_wgpu", 21 | "bevy_winit", 22 | "render", 23 | "png", 24 | "hdr", 25 | "x11" 26 | ] 27 | default-features = false 28 | 29 | [dev-dependencies] 30 | log = "0.4.14" 31 | env_logger = "0.8.2" 32 | 33 | [target.'cfg(target_os = "android")'.dependencies] 34 | ndk-glue = "0.3" 35 | 36 | [package.metadata.android] 37 | apk_label = "Bevy OpenXR wgpu" 38 | build_targets = ["aarch64-linux-android"] 39 | assets = "assets" 40 | runtime_libs = "libs" 41 | min_sdk_version = 16 42 | target_sdk_version = 29 43 | 44 | [[package.metadata.android.application.meta_data]] 45 | name = "com.samsung.android.vr.application.mode" 46 | value = "vr_only" 47 | 48 | [[package.metadata.android.application.meta_data]] 49 | name = "com.oculus.supportedDevices" 50 | value = "quest|quest2" 51 | 52 | [package.metadata.android.application.activity] 53 | theme = "@android:style/Theme.Black.NoTitleBar.Fullscreen" 54 | config_changes = "density|keyboard|keyboardHidden|navigation|orientation|screenLayout|screenSize|uiMode" 55 | launch_mode = "singleTask" 56 | orientation = "landscape" 57 | resizeable_activity = "false" 58 | 59 | [[package.metadata.android.application.activity.meta_data]] 60 | name = "com.oculus.vr.focusaware" 61 | value = "true" 62 | 63 | [[package.metadata.android.application.activity.intent_filter]] 64 | actions = ["android.intent.action.MAIN"] 65 | categories = ["com.oculus.intent.category.VR", "android.intent.category.LAUNCHER"] 66 | 67 | [[package.metadata.android.uses_permission]] 68 | name = "com.oculus.permission.HAND_TRACKING" 69 | 70 | [[package.metadata.android.uses_feature]] 71 | name = "oculus.software.handtracking" 72 | required = false 73 | 74 | [profile.release] 75 | opt-level = 3 76 | #lto = true 77 | 78 | [[example]] 79 | name = "xr_apk_scene" 80 | crate-type = ["cdylib"] 81 | 82 | [[example]] 83 | name = "xr_pc_scene" 84 | 85 | [[example]] 86 | name = "non_xr_scene" 87 | 88 | [patch.crates-io] 89 | bevy = { path = "./repos/bevy" } 90 | openxr = { path = "./repos/openxrs/openxr" } 91 | openxr-sys = { path = "./repos/openxrs/sys" } 92 | wgpu = { path = "./repos/wgpu-rs" } 93 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | download_dependencies: 2 | mkdir -p repos 3 | for i in android-ndk-rs bevy bevy_rapier gfx openxrs wgpu wgpu-rs; \ 4 | do \ 5 | echo "==== $$i =========="; \ 6 | if [ ! -d "$$i" ]; then \ 7 | git -C repos clone https://github.com/blaind/$$i.git; \ 8 | git -C repos/$$i fetch origin; \ 9 | git -C repos/$$i checkout bevy_openxr; \ 10 | fi; \ 11 | done; 12 | 13 | git -C repos clone https://github.com/blaind/bevy_openxr.git 14 | 15 | update_dependencies: 16 | for i in android-ndk-rs bevy bevy_rapier gfx openxrs wgpu wgpu-rs; \ 17 | do \ 18 | git -C repos/$$i pull; \ 19 | done; 20 | 21 | git -C repos/bevy_openxr pull 22 | git pull 23 | 24 | diff: 25 | for i in android-ndk-rs bevy bevy_rapier gfx openxrs wgpu wgpu-rs bevy_openxr; \ 26 | do \ 27 | git -C repos/$$i diff; \ 28 | done; 29 | 30 | adb_logcat: 31 | $$ANDROID_SDK_ROOT/platform-tools/adb -d logcat |grep Rust 32 | 33 | adb_shell: 34 | $$ANDROID_SDK_ROOT/platform-tools/adb -d shell 35 | 36 | run_xr_apk: 37 | CARGO_BUILD_TARGET_DIR=apk_target cargo apk run --example xr_apk_scene --release 38 | 39 | run_xr_pc: 40 | cargo run --example xr_pc_scene 41 | 42 | restart_apk: 43 | $$ANDROID_SDK_ROOT/platform-tools/adb shell am force-stop rust.example.xr_apk_scene 44 | $$ANDROID_SDK_ROOT/platform-tools/adb shell am start -a android.intent.action.MAIN -n rust.example.xr_apk_scene/android.app.NativeActivity 45 | 46 | 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Proof-of-concept of getting [OpenXR](https://www.khronos.org/openxr/) rendering 4 | support for [Bevy](https://github.com/bevyengine/bevy) game engine using 5 | [gfx-rs](https://github.com/gfx-rs/gfx/) abstractions. 6 | 7 | ![Example](docs/screenshot.webp) 8 | 9 | **This repository is obsolete, see 10 | [https://github.com/awtterpip/bevy_openxr](https://github.com/awtterpip/bevy_openxr) 11 | for current implementation** 12 | 13 | ## Install 14 | 15 | ### Windows 16 | 17 | Tested with Oculus Quest 2 using Oculus Link in Virtual Desktop mode (must be 18 | enabled before starting app). 19 | 20 | Clone this repository and run 21 | [install_dependencies.bat](./scripts/install_dependencies.bat) to download 22 | required dependencies (patched bevy, wgpu, openxrs crates, etc.): 23 | 24 | git clone https://github.com/blaind/xrbevy.git 25 | cd xrbevy 26 | .\scripts\install_dependencies.bat 27 | 28 | Run the example scene using [Rust](https://www.rust-lang.org/). 29 | 30 | cargo run --example xr_pc_scene --release 31 | 32 | If you don't already have an openxr loader, the process will complain about 33 | missing `openxr_loader.dll` file. One way to have a loader is to use a Khronos 34 | loader: 35 | 36 | 1. Navigate to https://github.com/KhronosGroup/OpenXR-SDK-Source/releases/ 37 | 1. Look for the latest release and a file called 38 | `openxr_loader_windows-[version].zip` 39 | 1. Copy `openxr_loader_windows\x64\bin\openxr_loader.dll` to `xrbevy` folder 40 | (from zip) 41 | 42 | The loader must also be configured to point into correct runtime. For Oculus, 43 | first install [Oculus app](https://www.oculus.com/setup/) and change the active 44 | runtime through a registry editor. Open a terminal as administrator, and run: 45 | 46 | # Print current values 47 | reg query HKEY_LOCAL_MACHINE\SOFTWARE\Khronos\OpenXR\1 48 | 49 | # Modify ActiveRuntime value to Oculus: 50 | reg add HKEY_LOCAL_MACHINE\SOFTWARE\Khronos\OpenXR\1 /v ActiveRuntime /d "C:\Program Files\oculus\Support\oculus-runtime\oculus_openxr_64.json" 51 | 52 | If errors, see [troubleshooting](#troubleshooting) 53 | 54 | In future, to get the latest changes: 55 | 56 | .\scripts\update_dependencies.bat 57 | 58 | ### Ubuntu 59 | 60 | **Note: tested only with Monado in virtual desktop mode, not with real 61 | hardware** 62 | 63 | Install needed dependencies: 64 | 65 | sudo apt-get install make libudev-dev libasound2-dev 66 | 67 | Clone this repository and run a [Makefile](./Makefile) command to download 68 | required dependencies (patched bevy, wgpu, openxrs crates, etc.): 69 | 70 | git clone https://github.com/blaind/xrbevy.git 71 | cd xrbevy 72 | make download_dependencies 73 | 74 | Run the example scene using [Rust](https://www.rust-lang.org/): 75 | 76 | cargo run --example xr_pc_scene --release 77 | 78 | If you don't already have an openxr loader, the process will complain about 79 | missing `openxr_loader.dll` file. Please try first One way to install a loader 80 | is to use [Monado](https://monado.dev/) loader. Ubuntu installation: 81 | 82 | sudo add-apt-repository ppa:monado-xr/monado 83 | sudo apt-get update 84 | sudo apt-get install libopenxr-loader1 85 | 86 | If you still get the error after retrying cargo run, try: 87 | 88 | sudo ln -s /usr/lib/x86_64-linux-gnu/libopenxr_loader.so.1 /usr/local/lib/libopenxr_loader.so && sudo ldconfig 89 | 90 | In addition to a loader, you'll need an OpenXR runtime: either virtualized like 91 | Monado, or real like **maybe** (untested) SteamVR. For Monado, see 92 | https://monado.freedesktop.org/getting-started.html#monado. If you find a way to 93 | run with real hardware, please contact author or make a pull request to document 94 | the steps here. 95 | 96 | If errors, see [troubleshooting](#troubleshooting) 97 | 98 | In future, to get the latest changes: 99 | 100 | make update_dependencies 101 | 102 | ### Mac OS 103 | 104 | Currently untested. Please make a pull request if you get it working. 105 | 106 | ### Oculus Quest 2 107 | 108 | Please note, that currently Quest 2 has a bug after exiting from app - might 109 | require restart of the device or reinstall of the app to relaunch. 110 | 111 | See [Oculus installation instructions](./docs/install_oculus.md) 112 | 113 | # Background 114 | 115 | Targets: 116 | 117 | 1. Demonstrate that OpenXR rendering is possible on Rust/GFX-RS/Bevy -ecosystem 118 | 1. Start the discussion with ecosystem participants about proper way to 119 | implement OpenXR support for Rust 120 | 1. Eventually have the basic building blocks in an easy-to-use state for 121 | building XR apps on top of bevy, or implementing XR support for other 122 | rendering engines 123 | 124 | For technical details, see [docs/architecture.md](docs/architecture.md): 125 | 126 | 127 |
128 | 129 | Bevy OpenXR plugin: 130 | 131 | - bevy_openxr: https://github.com/blaind/bevy_openxr 132 | 133 | Currently known pull requests for XR integration: 134 | 135 | - bevy: ~~https://github.com/bevyengine/bevy/pull/2166~~ 136 | https://github.com/bevyengine/bevy/pull/2319 137 | - wgpu-rs: ~~https://github.com/gfx-rs/wgpu-rs/pull/910~~ (see wgpu below) 138 | - wgpu: ~~https://github.com/gfx-rs/wgpu/pull/1387~~ 139 | https://github.com/gfx-rs/wgpu/pull/1609 140 | - gfx-rs: ~~https://github.com/gfx-rs/gfx/pull/3761~~ 141 | https://github.com/gfx-rs/gfx/pull/3762 142 | 143 | A few miscellanceous / support crates: 144 | 145 | - bevy_rapier: https://github.com/dimforge/bevy_rapier/pull/69 (only required 146 | for kinematics in the example) 147 | - android-ndk-rs: https://github.com/rust-windowing/android-ndk-rs/pull/138 148 | (needed for bundling .so for Oculus Quest 2) 149 | 150 | # Related material 151 | 152 | Further reading - some links that have helped in getting this PoC working: 153 | 154 | - https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html 155 | - https://github.com/Ralith/openxrs/blob/master/openxr/examples/vulkan.rs 156 | - https://github.com/GodotVR/godot_openxr 157 | 158 | # Community 159 | 160 | Check out [Bevy Discord](https://discord.gg/gMUk5Ph) channel `#xr` 161 | 162 | # Troubleshooting 163 | 164 | ## ERROR_FORM_FACTOR_UNAVAILABLE 165 | 166 | thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ERROR_FORM_FACTOR_UNAVAILABLE', repos\gfx\src\backend\vulkan\src\xr.rs:63:14 167 | 168 | Solution: Make sure that your device is connected 169 | 170 | ## OpenXR loader not found 171 | 172 | Could not load openxr loader. Make sure that you have openxr_loader.dll (Windows), libopenxr_loader.dylib (MacOS) or libopenxr_loader.so (Linux) in the library load path 173 | 174 | Solution: follow the platform-specific installation instructions, and make sure 175 | that one of the above files is present in library load path (or current path). 176 | 177 | ## Unknown wgpu/OpenXR extension 178 | 179 | ``` 180 | ADD INSTANCE EXTENSION! "VK_KHR_external_memory_capabilities" 181 | ADD INSTANCE EXTENSION! "VK_KHR_external_fence_capabilities" 182 | ADD INSTANCE EXTENSION! "VK_KHR_external_semaphore_capabilities" 183 | ADD DEVICE EXTENSION! "VK_KHR_external_memory" 184 | ADD DEVICE EXTENSION! "VK_KHR_external_memory_win32" 185 | thread 'main' panicked at 'Unknown wgpu/OpenXR extension, add to openxr_extensions! macro: Unknown', repos\gfx\src\backend\vulkan\src\xr.rs:233:26 186 | ``` 187 | 188 | Solution: some of the extensions is missing from xr code. Please open a new 189 | issue to this repository, or make a pull request directly into: 190 | https://github.com/blaind/gfx/blob/bevy_openxr/src/backend/vulkan/src/xr.rs#L326 191 | (last part with `openx_extensions!` macro). The format is as: 192 | 193 | (VK_KHR_EXTERNAL_SEMAPHORE_WIN32, "VK_KHR_external_semaphore_win32", 1048576), 194 | ^^^ all uppercase ^^ format as specific by OpenXR ^^ pow2 195 | 196 | For the pow2 number, take the max/latest number and multiply it by two to get a 197 | next value. 198 | 199 | ## No OpenXR runtime found 200 | 201 | Error [GENERAL | | OpenXR-Loader] : RuntimeManifestFile::FindManifestFiles - failed to find active runtime file in registry 202 | Error [GENERAL | xrEnumerateInstanceExtensionProperties | OpenXR-Loader] : RuntimeInterface::LoadRuntimes - unknown error 203 | Error [GENERAL | xrEnumerateInstanceExtensionProperties | OpenXR-Loader] : RuntimeInterface::LoadRuntimes - failed to load a runtime 204 | Error [GENERAL | xrEnumerateInstanceExtensionProperties | OpenXR-Loader] : Failed to find default runtime with RuntimeInterface::LoadRuntime() 205 | Error [GENERAL | xrEnumerateInstanceExtensionProperties | OpenXR-Loader] : Failed querying extension properties 206 | 207 | Solution/Linux: Modify `/etc/xdg/openxr/1/` files based on your runtime (see 208 | Google) 209 | 210 | Solution/Windows: See the `ActiveRuntime` part of the Windows setup before 211 | -------------------------------------------------------------------------------- /assets/textures/uv-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blaind/xrbevy/d8904aa2216362c3b1aa8e45ee8cf8e89019e162/assets/textures/uv-small.png -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_os = "android")] 2 | use std::path::Path; 3 | 4 | fn main() { 5 | #[cfg(target_os = "android")] 6 | if !Path::new("libs/aarch64-linux-android/libopenxr_loader.so").exists() { 7 | panic!("libs/aarch64-linux-android/libopenxr_loader.so is missing - see readme for instructions. This applies to Quest 2, if building for PC you can edit build.rs and commen out this line for now"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /docs/architecture.md: -------------------------------------------------------------------------------- 1 | # OpenXR architecture 2 | 3 | See https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html 4 | 5 | ![screenshot](screenshot3.png) 6 | 7 | # Bevy structure 8 | 9 | ![screenshot](screenshot0.png) 10 | 11 | ## bevy_openxr_core 12 | 13 | A core plugin. Responsibilities: 14 | * Device communication 15 | * Initialize the underlying openxr - device in co-operation with wgpu/gfx crates 16 | * Feature configuration? e.g. view mode, FPS, ... 17 | * Feature detection? 18 | * I/O 19 | * Output: Fetch and convert input data (controllers, hand tracking, hardware state) into bevy events 20 | * Input: (none currently, to be planned - use cases are for example haptics in controllers) 21 | 22 | 23 | Components: 24 | * struct `XRDevice`: currently mostly a wrapper around methods in the swapchain 25 | * struct `XRSwapchain`: Custom swapchain, which will handle rendering to views. Has prepare, post-update methods. etc. 26 | * struct `Framebuffer`: One per XR-camera (2 for headsets), contains a `wgpu::Texture` and `wgpu::TextureView` 27 | * struct `XRViewTransform`: (these are not actually stored inside, but rather there's a getter) -- used for storing per-camera (eye) positions. Like `bevy::Transform`, but has a helper method `compute_matrix()` which is called from `bevy::camera_node` 28 | * struct `HandTrackers`: left & right hand trackers 29 | * struct `HandPoseState`: if there are hand-trackers, `get_hand_positions()` method will return hand pose states 30 | * struct `OpenXRStruct`: poorly named struct, that contains various entities related to XR. Mostly used to bundle all into one `Option`, after `XRDevice` has been initialized. Will be built with `OpenXRStructBuilder` 31 | * enum `XRState`: current state of the underlying device (running, paused, etc) 32 | * struct `EventDataBufferHolder`: wraps `openxr::EventDataBuffer`, which contains newly produced (hardware?) events (`openxr::Event`) from openxr. App should react to these 33 | * struct `wgpu::OpenXRHandles`: container for various OpenXR-related items that are needed on bevy side. Moved from vulkan to bevy (see below) 34 | * struct `openxr::Instance`: Instance of openxr device. This instance is also used at gfx/vulkan side 35 | * struct `OpenXROptions`: configuration information (view type, trackers, etc.) 36 | 37 | * system `openxr_event_system`: transforms `openxr::Event` into bevy events (currently just `XRState` change-events) 38 | 39 | ## bevy_openxr 40 | 41 | Desired functionality must be scoped better. Initial rationale for dividing into separate crates was to prevent recursive loop between crates. 42 | 43 | Responsibilities? 44 | 45 | * Providing "simple" HandTracker & Controller plugins for visualizing (& interacting?) in the XR space. These will read the events from core crate, and create/update tracker meshes. Or should these be in separate crates? (`bevy_openxr_hand_tracking`, `bevy_openxr_controller_tracking`, ...) 46 | 47 | TODO: 48 | * `OpenXRInstance` and `XRProjection` should probably be located in the core crate. 49 | 50 | ## community crates 51 | 52 | * Fancy hand trackers, controller trackers, etc...? 53 | 54 | ## TODO 55 | 56 | What needs to happen in render loop: 57 | * Swapchain & texture rendering 58 | * Getting controller/hand positions 59 | * Getting head tracking state 60 | * etc.. 61 | 62 | # Initialization 63 | 64 | ## 1. Initializing OpenXR on app main 65 | 66 | This happens before bevy-code. 67 | 68 | * Construct `openxr::Entry` based on platform-specific code 69 | * Construct `openxr::Instance` 70 | 71 | ## 2. Copying `openxr::Instance` to gfx/vulkan and instantiating `WGPUOpenXR` 72 | 73 | This shows the initial OpenXR configuration flow from bevy_openxr to gfx-vulkan. As a part of this flow, Bevy will transfer a copy of `openxr::Instance` to gfx/vulkan. 74 | 75 | Notes: 76 | 77 | * Currently options are not passed from wgpu_core to vulkan. However, they are probably needed in future 78 | * Vulkan has a global singleton of type `Lazy>>`, that will contain the `openxr::Instance` (which wraps inner value into an `Arc`) 79 | 80 | ![screenshot](screenshot1.png) 81 | 82 | 83 | Bevy will get reference to all the openxr-related handles (inners are wrapped in `Arc`) 84 | 85 | For now, bevy will store the required xr-related structs in a global singleton (probably unsafe). 86 | 87 | ```rust 88 | pub fn set_openxr(wgpu_openxr: wgpu::wgpu_openxr::WGPUOpenXR, openxr_instance: openxr::Instance) { 89 | unsafe { 90 | WGPU_INSTANCE 91 | .set(WgpuData((wgpu_openxr, openxr_instance))) 92 | .unwrap() 93 | }; 94 | } 95 | ``` 96 | 97 | ## 3. Instantiating bevy-openxr (`OpenXRCorePlugin`) 98 | 99 | 1. Move `WGPUOpenXR` and `openxr::Instance` out of singleton 100 | 2. Build `OpenXRStruct`, for which handles are needed (see next step) 101 | 102 | (TODO: naming of all these structs is confusing, must come with better names) 103 | 104 | ## 4. Getting handles from gfx/vulkan 105 | 106 | After bevy has initialized itself, it will call through wgpu-rs to get an access to a few openxr-related handles. All of these are defined in `openxr` crate. 107 | 108 | Some are returned as moved entities, some are cloned (`Arc`) 109 | 110 | ![screenshot](screenshot2.png) 111 | 112 | gfx_backend_vulkan method for handles: 113 | ```rust 114 | pub fn get_session_handles() -> ( 115 | openxr::Session, 116 | openxr::FrameWaiter, 117 | openxr::FrameStream, 118 | openxr::Space, 119 | openxr::SystemId, 120 | ) { 121 | ``` 122 | 123 | OpenXRHandles used by bevy: 124 | ```rust 125 | pub struct OpenXRHandles { 126 | pub session: openxr::Session, 127 | pub frame_waiter: openxr::FrameWaiter, 128 | pub frame_stream: openxr::FrameStream, 129 | pub space: openxr::Space, 130 | pub system: openxr::SystemId, 131 | } 132 | ``` 133 | 134 | ## 5. Continue configuration on Bevy-side 135 | 136 | (TODO: document) 137 | 138 | ## 6. Construct `XRSwapchain` on bevy-side 139 | 140 | Instead of gfx swapchain, a custom swapchain is used. 141 | 142 | As a part of this call, a new wgpu method `device.create_openxr_texture_from_raw_image` will be called 143 | 144 | ## 7. Render loop 145 | 146 | (TODO: document) 147 | 148 | -------------------------------------------------------------------------------- /docs/graphs.md: -------------------------------------------------------------------------------- 1 | # screenshot 0 2 | ```mermaid 3 | graph LR 4 | style XRD fill:#555555,stroke:#fff,stroke-width:2px 5 | style XRSC fill:#555555,stroke:#fff,stroke-width:2px 6 | style OXRS fill:#555555,stroke:#fff,stroke-width:2px 7 | style XRS fill:#555555,stroke:#fff,stroke-width:2px 8 | style EDBH fill:#555555,stroke:#fff,stroke-width:2px 9 | style XRO fill:#555555,stroke:#fff,stroke-width:2px 10 | style XRHT fill:#555555,stroke:#fff,stroke-width:2px 11 | style XRI2 fill:#555555,stroke:#fff,stroke-width:2px 12 | 13 | style ScheduleRunnerSettings fill:#003300,stroke:#000,stroke-width:2px 14 | style XRH fill:#003300,stroke:#000,stroke-width:2px 15 | style XRI fill:#003300,stroke:#000,stroke-width:2px 16 | 17 | style BO fill:#003355,stroke:#000,stroke-width:2px 18 | style BOC fill:#003355,stroke:#000,stroke-width:2px 19 | 20 | style s_E fill:#ff9900,stroke:#000,stroke-width:2px,color:#333 21 | style s_CA fill:#ff9900,stroke:#000,stroke-width:2px,color:#333 22 | 23 | style XRCP fill:#00ff00,stroke:#000,stroke-width:2px,color:#333 24 | 25 | style XRP fill:#00ff00,stroke:#000,stroke-width:2px,color:#333 26 | 27 | BO[bevy_openxr] --- BOC[bevy_openxr_core] 28 | 29 | XRD --- XRSC[XRSwapchain] 30 | XRSC --- Framebuffer 31 | BOC --- XRD[XRDevice] 32 | BOC --- s_E[openxr_event_system] 33 | XRD --- OXRS[OpenXRStruct] 34 | OXRS --- XRS[XRState] 35 | OXRS --- EDBH[EventDataBufferHolder] 36 | OXRS --- XRH[wgpu::OpenXRHandles] 37 | OXRS --- XRI[openxr::Instance] 38 | OXRS --- XRO[OpenXROptions] 39 | XRSC --- XRVT[XRViewTransform] 40 | XRSC --- XRHT[HandTrackers] 41 | XRHT --- XRHPS[HandPoseState] 42 | 43 | BOC --- XRCP[OpenXRCorePlugin] 44 | 45 | BO --- XRP[OpenXRPlugin] 46 | 47 | XRP --- XRS2[OpenXRSettings] 48 | XRP --- PS[ProjectionState] 49 | XRP --- ScheduleRunnerSettings 50 | XRP --- s_CA[openxr_camera_system] 51 | XRP --- XRI2[OpenXRInstance] 52 | 53 | 54 | ``` 55 | 56 | # screenshot 1 57 | 58 | ```mermaid 59 | sequenceDiagram 60 | participant bevy_openxr 61 | participant wgpu_rs 62 | participant wgpu_core 63 | participant gfx_backend_vulkan 64 | 65 | bevy_openxr->>wgpu_rs: wgpu_openxr::new(.., instance, opt) 66 | wgpu_rs->>wgpu_core: WGPUOpenXR::configure(..,instance, opt) 67 | 68 | wgpu_core->>gfx_backend_vulkan: OpenXR::configure(instance) 69 | 70 | gfx_backend_vulkan->>gfx_backend_vulkan: initial openxr configuration 71 | gfx_backend_vulkan->>gfx_backend_vulkan: store instance to global Lazy>> 72 | 73 | gfx_backend_vulkan->>wgpu_core: OpenXR 74 | 75 | wgpu_core->>wgpu_rs: WGPUOpenXR 76 | 77 | wgpu_rs->>bevy_openxr: WGPUOpenXR 78 | 79 | bevy_openxr->>bevy_openxr: call set_openxr(), which will store to global OnceCell 80 | ``` 81 | 82 | # screenshot 2 83 | 84 | ```mermaid 85 | sequenceDiagram 86 | participant bevy_openxr 87 | participant wgpu_rs 88 | participant wgpu_core 89 | participant gfx_backend_vulkan 90 | 91 | bevy_openxr->>wgpu_core: call (local)wgpu_openxr.get_session_handles 92 | 93 | wgpu_core->gfx_backend_vulkan: call (static)get_session_handles() 94 | 95 | gfx_backend_vulkan->>wgpu_core: (Session, FrameWaiter, FrameStream, Space, SystemId) 96 | 97 | wgpu_core->>bevy_openxr: OpenXRHandles 98 | 99 | ``` 100 | 101 | # screenshot 3 102 | 103 | ```mermaid 104 | graph LR 105 | R[RUNTIME] 106 | I[xrInstance] --- S[xrSession] 107 | S --- SC[xrSwapchain] 108 | V[xrView] --- FOV[xrFovf] 109 | V --- POSE[xrPosef] 110 | R --- ALP[xrApiLayerProperties] 111 | EBM[xrEnvironmentBlendMode] 112 | SID[xrSystemId] 113 | FF[xrFormFactor] 114 | SPA[xrSpace] 115 | S --- RST[xrReferenceSpaceType] 116 | SID --- VCT[xrViewConfigurationType] 117 | 118 | I --- AS[xrActionSet] 119 | AS --- A[xrAction] 120 | S --- HT[xrHandTrackerEXT] 121 | R --- I 122 | S --- SPA 123 | SID --- FF 124 | S --- V 125 | 126 | R --- EP[xrExtensionProperties] 127 | 128 | FEI[xrFrameEndInfo] --- EBM 129 | S --- FEI 130 | 131 | A --- P[xrPath] 132 | A --- AT[xrActionType] 133 | 134 | I --- VKI[VkInstance] 135 | 136 | 137 | style R fill:#00ff00,stroke:#000,stroke-width:2px,color:#555 138 | style I fill:#ff8800,stroke:#000,stroke-width:2px,color:#fff 139 | style S fill:#ff8800,stroke:#000,stroke-width:2px,color:#fff 140 | style SC fill:#ff8800,stroke:#000,stroke-width:2px,color:#fff 141 | 142 | style ALP fill:#003300,stroke:#000,stroke-width:2px 143 | style EP fill:#003300,stroke:#000,stroke-width:2px 144 | style SID fill:#003300,stroke:#000,stroke-width:2px 145 | style FF fill:#003300,stroke:#000,stroke-width:2px 146 | style VCT fill:#003300,stroke:#000,stroke-width:2px 147 | style EBM fill:#003300,stroke:#000,stroke-width:2px 148 | style FOV fill:#003300,stroke:#000,stroke-width:2px 149 | style POSE fill:#003300,stroke:#000,stroke-width:2px 150 | style RST fill:#003300,stroke:#000,stroke-width:2px 151 | 152 | 153 | style AS fill:#555555,stroke:#fff,stroke-width:2px 154 | style A fill:#555555,stroke:#fff,stroke-width:2px 155 | style SPA fill:#555555,stroke:#fff,stroke-width:2px 156 | style HT fill:#555555,stroke:#fff,stroke-width:2px 157 | 158 | style V fill:#003355,stroke:#000,stroke-width:2px 159 | style FEI fill:#003355,stroke:#000,stroke-width:2px 160 | 161 | style VKI fill:#ffff00,stroke:#000,stroke-width:2px,color:#333 162 | 163 | ``` 164 | 165 | -------------------------------------------------------------------------------- /docs/graphs2.md: -------------------------------------------------------------------------------- 1 | # screenshot 1 2 | 3 | ```mermaid 4 | sequenceDiagram 5 | participant app 6 | participant entry 7 | participant xr_instance 8 | participant gfx_instance 9 | participant vk_phys_dev 10 | participant gfx_device 11 | participant xr_session 12 | participant xr_swapchain 13 | participant gfx_image 14 | 15 | Note right of app: Load XR instance, check for extensions, etc. 16 | rect rgb(50, 0, 50) 17 | app->>entry: A.1. 18 | activate entry 19 | activate entry 20 | entry-->>app: entry 21 | 22 | app->>+entry: A.5. enumerate_extensions() 23 | entry-->>-app: extensions 24 | 25 | app->>+entry: C.1. create_instance(&app_info, &enabled_extensions) 26 | 27 | entry->>+xr_instance: 28 | activate xr_instance 29 | 30 | xr_instance-->>entry: xr_instance 31 | entry-->>-app: xr_instance 32 | deactivate entry 33 | end 34 | 35 | Note right of app: Instantiate gfx-instance from raw vulkan instance received from XR 36 | rect rgb(50, 90, 30) 37 | app->>+xr_instance: E.1. create_vulkan_instance(...) 38 | xr_instance-->>-app: vk_instance 39 | 40 | app->>gfx_instance: G.1. 41 | activate gfx_instance 42 | gfx_instance-->>app: gfx_instance 43 | end 44 | 45 | Note right of app: Instantiate gfx-device device from raw vulkan physical dev from xr 46 | rect rgb(50, 90, 30) 47 | app->>+xr_instance: I.1. vulkan_graphics_device(gfx_instance.as_ptr()) 48 | xr_instance->>vk_phys_dev: 49 | activate vk_phys_dev 50 | vk_phys_dev->>xr_instance: vk_phys_dev 51 | deactivate vk_phys_dev 52 | xr_instance-->>-app: vk_phys_dev 53 | 54 | app->>gfx_device: K.1. 55 | activate gfx_device 56 | gfx_device-->>app: gfx_device 57 | end 58 | 59 | Note right of app: XR session is linked to quite a few gfx-instances (through raw ptrs) 60 | rect rgb(10, 10, 128) 61 | app->>+xr_instance: M.1. create_session(gfx_instance.as_ptr(), gfx_device.ptr(), gfx_device.ptr(), ...) 62 | 63 | xr_instance->>xr_session: 64 | activate xr_session 65 | 66 | xr_session-->>xr_instance: xr_session 67 | 68 | xr_instance-->>-app: xr_session 69 | end 70 | 71 | Note right of app: XR Swapchain occurs in Xr -space (what about internals?) 72 | rect rgb(50, 0, 50) 73 | app->>+xr_session: O.1. create_xr_swapchain(..) 74 | xr_session->>xr_swapchain: 75 | activate xr_swapchain 76 | xr_swapchain-->>xr_session: xr_swapchain 77 | xr_session-->>-app: xr_swapchain 78 | 79 | app->>+xr_swapchain: enumerate_images() 80 | xr_swapchain-->>-app: images 81 | end 82 | 83 | Note right of app: Swapchain contains image ptrs by runtime, convert to gfx images 84 | rect rgb(50, 90, 30) 85 | app->>gfx_image: P.1. 86 | activate gfx_image 87 | gfx_image-->>app: gfx_image 88 | end 89 | 90 | 91 | deactivate xr_session 92 | deactivate gfx_instance 93 | deactivate xr_instance 94 | deactivate xr_swapchain 95 | deactivate gfx_image 96 | deactivate gfx_device 97 | ``` 98 | 99 | 100 | ```mermaid 101 | sequenceDiagram 102 | participant app 103 | participant xr 104 | participant wgpu_rs 105 | participant wgpu 106 | participant gfx_backend_vulkan 107 | 108 | app->>xr: Entry::load() 109 | xr->>app: Entry 110 | 111 | app->>xr: Entry::create_instance(&app_info, &extensions) 112 | xr->>app: XrInstance 113 | 114 | 115 | 116 | app->>wgpu_rs: wgpu_openxr::new(.., instance, opt) 117 | wgpu_rs->>wgpu: WGPUOpenXR::configure(..,instance, opt) 118 | 119 | wgpu->>gfx_backend_vulkan: OpenXR::configure(instance) 120 | 121 | gfx_backend_vulkan->>gfx_backend_vulkan: initial openxr configuration 122 | gfx_backend_vulkan->>gfx_backend_vulkan: store instance to global Lazy>> 123 | 124 | gfx_backend_vulkan->>wgpu: OpenXR 125 | 126 | wgpu->>wgpu_rs: WGPUOpenXR 127 | 128 | wgpu_rs->>app: WGPUOpenXR 129 | 130 | app->>app: call set_openxr(), which will store to global OnceCell 131 | ``` 132 | -------------------------------------------------------------------------------- /docs/graphs3.md: -------------------------------------------------------------------------------- 1 | # screenshot 0 2 | ```mermaid 3 | graph LR 4 | style BW fill:#003355,stroke:#000,stroke-width:2px 5 | 6 | style OC fill:#003355,stroke:#000,stroke-width:2px 7 | 8 | style O fill:#003355,stroke:#000,stroke-width:2px 9 | 10 | style AB fill:#ff9900,stroke:#000,stroke-width:2px,color:#333 11 | 12 | style ORS fill:#001155,stroke:#000,stroke-width:2px 13 | 14 | style AK fill:#555555,stroke:#fff,stroke-width:2px 15 | 16 | style VU fill:#555555,stroke:#fff,stroke-width:2px 17 | 18 | style AC fill:#555555,stroke:#fff,stroke-width:2px 19 | 20 | style AC fill:#555555,stroke:#fff,stroke-width:2px 21 | 22 | style GR fill:#00ff00,stroke:#000,stroke-width:2px,color:#333 23 | 24 | style GRRS fill:#00ff00,stroke:#000,stroke-width:2px,color:#333 25 | 26 | style GFX fill:#00ff00,stroke:#000,stroke-width:2px,color:#333 27 | 28 | 29 | style GV fill:#003300,stroke:#000,stroke-width:2px 30 | 31 | style GH fill:#003300,stroke:#000,stroke-width:2px 32 | 33 | style GM fill:#003300,stroke:#000,stroke-width:2px 34 | 35 | style ash fill:#555555,stroke:#fff,stroke-width:2px 36 | 37 | style metal fill:#555555,stroke:#fff,stroke-width:2px 38 | 39 | style OX fill:#555555,stroke:#fff,stroke-width:2px 40 | 41 | 42 | BW[bevy_wgpu] --- OC[bevy_openxr_core] 43 | O[bevy_openxr] --- OC 44 | 45 | GRRS --- GR[wgpu] 46 | GR --- GFX[gfx] 47 | 48 | GFX --- GH[gfx_hal] 49 | GFX --- GV[gfx_backend_vulkan] 50 | GFX --- GM[gfx_metal] 51 | 52 | GV --- ash 53 | GM --- metal 54 | 55 | 56 | OC --- AB[some_abstraction_crate] 57 | AB --- OX[openxrs] 58 | 59 | AB --- GRRS[wgpu-rs] 60 | 61 | AB --- AK[arkit] 62 | AB --- VU[vuforia] 63 | AB --- AC[arcore] 64 | 65 | ORS[other_render_software] --- AB 66 | 67 | 68 | ``` -------------------------------------------------------------------------------- /docs/install_oculus.md: -------------------------------------------------------------------------------- 1 | # Building for Oculus Quest 2 2 | 3 | ## Prequisites 4 | 5 | Prequisite installation: 6 | 7 | sudo apt-get install make openjdk-11-jre g++ libudev-dev libasound2-dev gcc 8 | cargo install cargo-apk 9 | 10 | If you have not setup udev yet, copy udev rules for Oculus Quest 2 11 | 12 | # run both as root 13 | echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="2833", ATTR{idProduct}=="0186", MODE="0660", GROUP="plugdev"' > /etc/udev/rules.d/51-android.rules 14 | udevadm control --reload-rules && udevadm trigger 15 | 16 | 17 | ## Oculus OpenXR plugin 18 | 19 | You must download `libopenxr_loader.so` from [Oculus OpenXR Mobile SDK](https://developer.oculus.com/downloads/package/oculus-openxr-mobile-sdk/) file. It should be stored to `libs/arm64-v8a` folder. It is at path `OpenXR/Libs/Android/arm64-v8a/Release` in the zip file. 20 | 21 | $ ls -al libs/arm64-v8a/libopenxr_loader.so 22 | -rwxrwxr-x 1 user user 10639048 feb 13 14:46 libs/arm64-v8a/libopenxr_loader.so 23 | 24 | $ file libs/arm64-v8a/libopenxr_loader.so 25 | ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, BuildID[sha1]=..., with debug_info, not stripped 26 | 27 | ## Android SDK 28 | 29 | You need android SDK & NDK to build .apk. 30 | 31 | Append to `.bashrc` (and change paths if versions have changed...) 32 | 33 | export ANDROID_SDK_ROOT="$HOME/Android/Sdk" 34 | export ANDROID_NDK_ROOT="$HOME/Android/Sdk/ndk/22.0.7026061" 35 | 36 | Unzip https://developer.android.com/studio/command-line/sdkmanager to `$ANDROID_SDK_ROOT/cmdline-tools` 37 | 38 | After that, install required sdk's: 39 | 40 | ./cmdline-tools/bin/sdkmanager --sdk_root=$ANDROID_SDK_ROOT "ndk;22.0.7026061" 41 | ./cmdline-tools/bin/sdkmanager --sdk_root=$ANDROID_SDK_ROOT "build-tools;30.0.3" 42 | ./cmdline-tools/bin/sdkmanager --sdk_root=$ANDROID_SDK_ROOT "platforms;android-30" 43 | ./cmdline-tools/bin/sdkmanager --sdk_root=$ANDROID_SDK_ROOT "platforms;android-29" 44 | 45 | ## Run the example 46 | 47 | cargo apk run --example xr_apk_scene --release 48 | 49 | or 50 | 51 | make run_xr_apk 52 | 53 | If successful, it should output something like this: 54 | 55 | ``` 56 | Finished release [optimized] target(s) in 32.78s 57 | 'lib/arm64-v8a/libxr_apk_scene.so'... 58 | 'lib/arm64-v8a/libc++_shared.so'... 59 | 'lib/arm64-v8a/libopenxr_loader.so'... 60 | Verifying alignment of /.../Bevy OpenXR wgpu.apk (4)... 61 | 49 AndroidManifest.xml (OK - compressed) 62 | 1152 assets/textures/uv-small.png (OK) 63 | 319920 lib/arm64-v8a/libxr_apk_scene.so (OK - compressed) 64 | 15689936 lib/arm64-v8a/libc++_shared.so (OK - compressed) 65 | 17515002 lib/arm64-v8a/libopenxr_loader.so (OK - compressed) 66 | Verification succesful 67 | Performing Incremental Install 68 | Serving... 69 | Unknown command: install-incremental 70 | Performing Streamed Install 71 | Success 72 | Starting: Intent { act=android.intent.action.MAIN cmp=rust.example.xr_apk_scene/android.app.NativeActivity } 73 | ``` 74 | -------------------------------------------------------------------------------- /docs/screenshot.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blaind/xrbevy/d8904aa2216362c3b1aa8e45ee8cf8e89019e162/docs/screenshot.webp -------------------------------------------------------------------------------- /docs/screenshot0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blaind/xrbevy/d8904aa2216362c3b1aa8e45ee8cf8e89019e162/docs/screenshot0.png -------------------------------------------------------------------------------- /docs/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blaind/xrbevy/d8904aa2216362c3b1aa8e45ee8cf8e89019e162/docs/screenshot1.png -------------------------------------------------------------------------------- /docs/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blaind/xrbevy/d8904aa2216362c3b1aa8e45ee8cf8e89019e162/docs/screenshot2.png -------------------------------------------------------------------------------- /docs/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blaind/xrbevy/d8904aa2216362c3b1aa8e45ee8cf8e89019e162/docs/screenshot3.png -------------------------------------------------------------------------------- /examples/non_xr_scene.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | 3 | fn main() { 4 | App::build() 5 | .insert_resource(Msaa { samples: 4 }) 6 | .add_plugins(DefaultPlugins) 7 | .add_startup_system(setup.system()) 8 | .run(); 9 | } 10 | 11 | /// set up a simple 3D scene 12 | fn setup( 13 | mut commands: Commands, 14 | mut meshes: ResMut>, 15 | mut materials: ResMut>, 16 | ) { 17 | // add entities to the world 18 | commands 19 | // plane 20 | .spawn_bundle(PbrBundle { 21 | mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })), 22 | material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), 23 | ..Default::default() 24 | }); 25 | 26 | // cube 27 | commands.spawn_bundle(PbrBundle { 28 | mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), 29 | material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), 30 | transform: Transform::from_xyz(0.0, 0.5, 0.0), 31 | ..Default::default() 32 | }); 33 | 34 | // light 35 | commands.spawn_bundle(PointLightBundle { 36 | transform: Transform::from_xyz(4.0, 8.0, 4.0), 37 | ..Default::default() 38 | }); 39 | 40 | // camera 41 | commands.spawn_bundle(PerspectiveCameraBundle { 42 | transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::default(), Vec3::unit_y()), 43 | ..Default::default() 44 | }); 45 | } 46 | -------------------------------------------------------------------------------- /examples/xr_apk_scene.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use xrbevy::{is_already_running, ExampleScenePlugin}; 3 | 4 | #[cfg_attr(target_os = "android", ndk_glue::main(backtrace = "on"))] 5 | fn main() { 6 | if is_already_running() { 7 | // in android, main() can be called on resume 8 | // will create a new thread, but the process and existing App will be present 9 | println!("Already running - quit call to main()"); 10 | } else { 11 | App::new() 12 | .add_plugin(ExampleScenePlugin) 13 | .add_plugin(bevy_openxr::OpenXRHandTrackingPlugin) 14 | .run(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/xr_pc_scene.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | 3 | use xrbevy::ExampleScenePlugin; 4 | 5 | fn main() { 6 | std::env::set_var("RUST_LOG", "warn"); 7 | env_logger::init(); 8 | 9 | App::new().add_plugin(ExampleScenePlugin).run(); 10 | } 11 | -------------------------------------------------------------------------------- /libs/arm64-v8a/.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | -------------------------------------------------------------------------------- /libs/arm64-v8a/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blaind/xrbevy/d8904aa2216362c3b1aa8e45ee8cf8e89019e162/libs/arm64-v8a/.gitkeep -------------------------------------------------------------------------------- /scripts/install_dependencies.bat: -------------------------------------------------------------------------------- 1 | mkdir repos 2 | 3 | for %%s in ("android-ndk-rs", "bevy", "bevy_rapier", "gfx", "openxrs", "wgpu", "wgpu-rs") do ( 4 | git -C repos clone https://github.com/blaind/%%s.git 5 | git -C repos/%%s fetch origin 6 | git -C repos/%%s checkout bevy_openxr 7 | ) 8 | 9 | git -C repos clone https://github.com/blaind/bevy_openxr 10 | -------------------------------------------------------------------------------- /scripts/update_dependencies.bat: -------------------------------------------------------------------------------- 1 | mkdir repos 2 | 3 | for %%s in ("android-ndk-rs", "bevy", "bevy_rapier", "gfx", "openxrs", "wgpu", "wgpu-rs") do ( 4 | git -C repos/%%s pull 5 | ) 6 | 7 | git -C repos/bevy_openxr pull 8 | git pull -------------------------------------------------------------------------------- /src/example_scene_plugin.rs: -------------------------------------------------------------------------------- 1 | use bevy_openxr::prelude::*; 2 | use bevy::prelude::*; 3 | 4 | pub struct ExampleScenePlugin; 5 | 6 | impl Plugin for ExampleScenePlugin { 7 | fn build(&self, app: &mut App) { 8 | app.insert_resource(Msaa { samples: 2 }) // FIXME openxr crashes if samples > 1 9 | .add_plugin(bevy_openxr::OpenXRPlugin) 10 | .add_plugins(DefaultPlugins) 11 | .add_plugin(bevy_openxr_core::OpenXRCorePlugin) 12 | .add_plugin(bevy_openxr::OpenXRWgpuPlugin) 13 | .add_startup_system(setup.system()); 14 | } 15 | } 16 | 17 | fn setup( 18 | mut commands: Commands, 19 | asset_server: Res, 20 | mut meshes: ResMut>, 21 | mut materials: ResMut>, 22 | ) { 23 | let small_uv = asset_server.load("textures/uv-small.png"); 24 | let small_uv_material = materials.add(StandardMaterial { 25 | base_color_texture: Some(small_uv.clone()), 26 | unlit: false, 27 | ..Default::default() 28 | }); 29 | 30 | commands.spawn_bundle(PbrBundle { 31 | mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })), 32 | material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), 33 | ..Default::default() 34 | }); 35 | 36 | commands.spawn_bundle(PointLightBundle { 37 | point_light: PointLight { 38 | color: Color::rgb(1., 1., 1.), 39 | ..Default::default() 40 | }, 41 | transform: Transform::from_xyz(4.0, 8.0, 4.0), 42 | ..Default::default() 43 | }); 44 | commands.spawn_bundle(XRCameraBundle::default()); 45 | 46 | commands.spawn_bundle(PbrBundle { 47 | mesh: meshes.add(Mesh::from(shape::Cube { size: 0.3 })), 48 | material: small_uv_material, 49 | transform: Transform::from_xyz(0., 0.8, -0.7), 50 | ..Default::default() 51 | }); 52 | 53 | let color_material = materials.add(Color::rgb(0.8, 0.7, 0.6).into()); 54 | 55 | // for now, do not draw cubes as they slow down 56 | return; 57 | 58 | for y in -3..3 { 59 | for x in -3..3 { 60 | for z in -3..3 { 61 | if x == 0 && z == 0 { 62 | continue; 63 | } 64 | 65 | let spacing = 0.5; 66 | 67 | commands.spawn_bundle(PbrBundle { 68 | mesh: meshes.add(Mesh::from(shape::Cube { size: 0.1 })), 69 | material: color_material.clone(), 70 | transform: Transform::from_xyz( 71 | x as f32 * spacing, 72 | y as f32 * spacing, 73 | z as f32 * spacing, 74 | ), 75 | ..Default::default() 76 | }); 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod example_scene_plugin; 2 | pub use example_scene_plugin::*; 3 | 4 | use std::sync::atomic::{AtomicBool, Ordering}; 5 | static IS_STARTED: AtomicBool = AtomicBool::new(false); 6 | 7 | pub fn is_already_running() -> bool { 8 | if IS_STARTED.load(Ordering::SeqCst) { 9 | return true; 10 | } 11 | IS_STARTED.store(true, Ordering::SeqCst); 12 | false 13 | } 14 | --------------------------------------------------------------------------------