Merge pull request #18 from alexichepura/quest
Android, Meta Quest support
This commit is contained in:
3
examples/android/.gitignore
vendored
Normal file
3
examples/android/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/target
|
||||||
|
/Cargo.lock
|
||||||
|
/runtime_libs
|
||||||
67
examples/android/Cargo.toml
Normal file
67
examples/android/Cargo.toml
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
[package]
|
||||||
|
name = "bevy_openxr_android"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
description = "Example for building an Android OpenXR app with Bevy"
|
||||||
|
publish = false
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "bevy_openxr_android"
|
||||||
|
crate-type = ["staticlib", "cdylib"]
|
||||||
|
|
||||||
|
[package.metadata.android]
|
||||||
|
package = "org.bevyengine.example_openxr_android"
|
||||||
|
build_targets = ["aarch64-linux-android"]
|
||||||
|
runtime_libs = "runtime_libs"
|
||||||
|
apk_name = "bevyopenxr"
|
||||||
|
# assets = "assets"
|
||||||
|
# res = "assets/android-res"
|
||||||
|
icon = "@mipmap/ic_launcher"
|
||||||
|
label = "Bevy Openxr Android"
|
||||||
|
strip = "strip"
|
||||||
|
|
||||||
|
[package.metadata.android.sdk]
|
||||||
|
target_sdk_version = 32
|
||||||
|
|
||||||
|
# [package.metadata.android.application]
|
||||||
|
# icon = "@mipmap/ic_launcher"
|
||||||
|
# label = "Bevy Example"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bevy_openxr = { path = "../..", default-features = false }
|
||||||
|
bevy = { git = "https://github.com/bevyengine/bevy.git" }
|
||||||
|
openxr = { git = "https://github.com/Ralith/openxrs", features = ["mint"] }
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = "fat"
|
||||||
|
codegen-units = 1
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[package.metadata.android.application.activity]
|
||||||
|
theme = "@android:style/Theme.Black.NoTitleBar.Fullscreen"
|
||||||
|
config_changes = "density|keyboard|keyboardHidden|navigation|orientation|screenLayout|screenSize|uiMode"
|
||||||
|
launch_mode = "singleTask"
|
||||||
|
orientation = "landscape"
|
||||||
|
resizeable_activity = false
|
||||||
|
|
||||||
|
[[package.metadata.android.application.activity.intent_filter]]
|
||||||
|
actions = ["android.intent.action.MAIN"]
|
||||||
|
categories = [
|
||||||
|
"com.oculus.intent.category.VR",
|
||||||
|
"android.intent.category.LAUNCHER",
|
||||||
|
]
|
||||||
|
|
||||||
|
# !! IMPORTANT !!
|
||||||
|
#
|
||||||
|
# When creating your own apps, make sure to generate your own keystore, rather than using our example one!
|
||||||
|
# You can use `keytool` like so:
|
||||||
|
# keytool -genkey -v -keystore my-release-key.keystore -keyalg RSA -keysize 2048 -validity 10000
|
||||||
|
#
|
||||||
|
# For more information on key signing and why it's so important, check out this article:
|
||||||
|
# https://developer.android.com/studio/publish/app-signing
|
||||||
|
#
|
||||||
|
# !! IMPORTANT !!
|
||||||
|
[package.metadata.android.signing.release]
|
||||||
|
path = "./hotham_examples.keystore"
|
||||||
|
keystore_password = "chomsky-vigilant-spa"
|
||||||
46
examples/android/README.md
Normal file
46
examples/android/README.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# Bevy OpenXR Android example
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
Get libopenxr_loader.so from the Oculus OpenXR Mobile SDK and add it to `examples/android/runtime_libs/arm64-v8a`
|
||||||
|
https://developer.oculus.com/downloads/package/oculus-openxr-mobile-sdk/
|
||||||
|
`examples/android/runtime_libs/arm64-v8a/libopenxr_loader.so`
|
||||||
|
|
||||||
|
Running on Meta Quest can be done with https://github.com/rust-mobile/cargo-apk.
|
||||||
|
```sh
|
||||||
|
cargo apk run --release
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
### Relase mode
|
||||||
|
More optimisations enabled in Cargo.toml for the release mode.
|
||||||
|
This gives more performance but longer build time.
|
||||||
|
```toml
|
||||||
|
[profile.release]
|
||||||
|
lto = "fat"
|
||||||
|
codegen-units = 1
|
||||||
|
panic = "abort"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cargo apk
|
||||||
|
If you see error like `Error: String `` is not a PID`, try to install cargo apk with a fix in branch.
|
||||||
|
```sh
|
||||||
|
cargo install --git https://github.com/rust-mobile/cargo-apk --branch=adb-logcat-uid
|
||||||
|
```
|
||||||
|
|
||||||
|
### Temporary JNIEnv log
|
||||||
|
This message is logged every frame. It's not yet fixed.
|
||||||
|
```sh
|
||||||
|
I JniUtils-inl: Creating temporary JNIEnv. This is a heavy operation and should be infrequent. To optimize, use JNI AttachCurrentThread on calling threa
|
||||||
|
```
|
||||||
|
|
||||||
|
### Android keystore
|
||||||
|
Release mode requires keystore. See Cargo.toml `package.metadata.android.signing.release`.
|
||||||
|
|
||||||
|
When creating your own apps, make sure to generate your own keystore, rather than using our example one!
|
||||||
|
You can use `keytool` like so:
|
||||||
|
```sh
|
||||||
|
keytool -genkey -v -keystore my-release-key.keystore -keyalg RSA -keysize 2048 -validity 10000
|
||||||
|
```
|
||||||
|
For more information on key signing and why it's so important, check out this article:
|
||||||
|
https://developer.android.com/studio/publish/app-signing
|
||||||
BIN
examples/android/hotham_examples.keystore
Normal file
BIN
examples/android/hotham_examples.keystore
Normal file
Binary file not shown.
83
examples/android/src/lib.rs
Normal file
83
examples/android/src/lib.rs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
use bevy::diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin};
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy::transform::components::Transform;
|
||||||
|
use bevy_openxr::xr_input::debug_gizmos::OpenXrDebugRenderer;
|
||||||
|
use bevy_openxr::xr_input::prototype_locomotion::{proto_locomotion, PrototypeLocomotionConfig};
|
||||||
|
use bevy_openxr::xr_input::trackers::{
|
||||||
|
OpenXRController, OpenXRLeftController, OpenXRRightController, OpenXRTracker,
|
||||||
|
};
|
||||||
|
use bevy_openxr::DefaultXrPlugins;
|
||||||
|
|
||||||
|
#[bevy_main]
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.add_plugins(DefaultXrPlugins)
|
||||||
|
.add_plugins(OpenXrDebugRenderer)
|
||||||
|
.add_plugins(LogDiagnosticsPlugin::default())
|
||||||
|
.add_plugins(FrameTimeDiagnosticsPlugin)
|
||||||
|
.add_systems(Startup, setup)
|
||||||
|
.add_systems(Update, proto_locomotion)
|
||||||
|
.add_systems(Startup, spawn_controllers_example)
|
||||||
|
.insert_resource(PrototypeLocomotionConfig::default())
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// set up a simple 3D scene
|
||||||
|
fn setup(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
) {
|
||||||
|
// plane
|
||||||
|
commands.spawn(PbrBundle {
|
||||||
|
mesh: meshes.add(shape::Plane::from_size(5.0).into()),
|
||||||
|
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
// cube
|
||||||
|
commands.spawn(PbrBundle {
|
||||||
|
mesh: meshes.add(Mesh::from(shape::Cube { size: 0.1 })),
|
||||||
|
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
|
||||||
|
transform: Transform::from_xyz(0.0, 0.5, 0.0),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
// cube
|
||||||
|
commands.spawn(PbrBundle {
|
||||||
|
mesh: meshes.add(Mesh::from(shape::Cube { size: 0.1 })),
|
||||||
|
material: materials.add(Color::rgb(0.8, 0.0, 0.0).into()),
|
||||||
|
transform: Transform::from_xyz(0.0, 0.5, 1.0),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
// light
|
||||||
|
commands.spawn(PointLightBundle {
|
||||||
|
point_light: PointLight {
|
||||||
|
intensity: 1500.0,
|
||||||
|
shadows_enabled: true,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
transform: Transform::from_xyz(4.0, 8.0, 4.0),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
// camera
|
||||||
|
// commands.spawn((Camera3dBundle {
|
||||||
|
// transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
|
// ..default()
|
||||||
|
// },));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn_controllers_example(mut commands: Commands) {
|
||||||
|
//left hand
|
||||||
|
commands.spawn((
|
||||||
|
OpenXRLeftController,
|
||||||
|
OpenXRController,
|
||||||
|
OpenXRTracker,
|
||||||
|
SpatialBundle::default(),
|
||||||
|
));
|
||||||
|
//right hand
|
||||||
|
commands.spawn((
|
||||||
|
OpenXRRightController,
|
||||||
|
OpenXRController,
|
||||||
|
OpenXRTracker,
|
||||||
|
SpatialBundle::default(),
|
||||||
|
));
|
||||||
|
}
|
||||||
@@ -42,6 +42,9 @@ pub fn initialize_xr_graphics(
|
|||||||
|
|
||||||
let xr_entry = super::xr_entry();
|
let xr_entry = super::xr_entry();
|
||||||
|
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
xr_entry.initialize_android_loader().unwrap();
|
||||||
|
|
||||||
let available_extensions = xr_entry.enumerate_extensions()?;
|
let available_extensions = xr_entry.enumerate_extensions()?;
|
||||||
assert!(available_extensions.khr_vulkan_enable2);
|
assert!(available_extensions.khr_vulkan_enable2);
|
||||||
info!("available xr exts: {:#?}", available_extensions);
|
info!("available xr exts: {:#?}", available_extensions);
|
||||||
@@ -82,8 +85,16 @@ pub fn initialize_xr_graphics(
|
|||||||
|
|
||||||
let blend_mode = xr_instance.enumerate_environment_blend_modes(xr_system_id, VIEW_TYPE)?[0];
|
let blend_mode = xr_instance.enumerate_environment_blend_modes(xr_system_id, VIEW_TYPE)?[0];
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "android"))]
|
||||||
let vk_target_version = vk::make_api_version(0, 1, 2, 0);
|
let vk_target_version = vk::make_api_version(0, 1, 2, 0);
|
||||||
|
#[cfg(not(target_os = "android"))]
|
||||||
let vk_target_version_xr = xr::Version::new(1, 2, 0);
|
let vk_target_version_xr = xr::Version::new(1, 2, 0);
|
||||||
|
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
let vk_target_version = vk::make_api_version(0, 1, 1, 0);
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
let vk_target_version_xr = xr::Version::new(1, 1, 0);
|
||||||
|
|
||||||
let reqs = xr_instance.graphics_requirements::<xr::Vulkan>(xr_system_id)?;
|
let reqs = xr_instance.graphics_requirements::<xr::Vulkan>(xr_system_id)?;
|
||||||
if vk_target_version_xr < reqs.min_api_version_supported
|
if vk_target_version_xr < reqs.min_api_version_supported
|
||||||
|| vk_target_version_xr.major() > reqs.max_api_version_supported.major()
|
|| vk_target_version_xr.major() > reqs.max_api_version_supported.major()
|
||||||
@@ -102,6 +113,8 @@ pub fn initialize_xr_graphics(
|
|||||||
let device_extensions = vec![
|
let device_extensions = vec![
|
||||||
ash::extensions::khr::Swapchain::name(),
|
ash::extensions::khr::Swapchain::name(),
|
||||||
ash::extensions::khr::DrawIndirectCount::name(),
|
ash::extensions::khr::DrawIndirectCount::name(),
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
ash::extensions::khr::TimelineSemaphore::name(),
|
||||||
];
|
];
|
||||||
info!(
|
info!(
|
||||||
"creating vulkan instance with these extensions: {:#?}",
|
"creating vulkan instance with these extensions: {:#?}",
|
||||||
|
|||||||
@@ -201,10 +201,17 @@ impl PluginGroup for DefaultXrPlugins {
|
|||||||
.add_before::<RenderPlugin, _>(OpenXrPlugin)
|
.add_before::<RenderPlugin, _>(OpenXrPlugin)
|
||||||
.add_after::<OpenXrPlugin, _>(OpenXrInput::new(XrControllerType::OculusTouch))
|
.add_after::<OpenXrPlugin, _>(OpenXrInput::new(XrControllerType::OculusTouch))
|
||||||
.set(WindowPlugin {
|
.set(WindowPlugin {
|
||||||
|
#[cfg(not(target_os = "android"))]
|
||||||
primary_window: Some(Window {
|
primary_window: Some(Window {
|
||||||
present_mode: PresentMode::AutoNoVsync,
|
present_mode: PresentMode::AutoNoVsync,
|
||||||
..default()
|
..default()
|
||||||
}),
|
}),
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
primary_window: None,
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
exit_condition: bevy::window::ExitCondition::DontExit,
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
close_when_requested: true,
|
||||||
..default()
|
..default()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user