Skip to content

Commit

Permalink
Merge pull request #18 from alexichepura/quest
Browse files Browse the repository at this point in the history
Android, Meta Quest support
  • Loading branch information
ForTehLose authored Oct 18, 2023
2 parents 6f3272e + d77d196 commit 879c40f
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 0 deletions.
3 changes: 3 additions & 0 deletions examples/android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/target
/Cargo.lock
/runtime_libs
67 changes: 67 additions & 0 deletions examples/android/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 changes: 46 additions & 0 deletions examples/android/README.md
Original file line number Diff line number Diff line change
@@ -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
Binary file added examples/android/hotham_examples.keystore
Binary file not shown.
83 changes: 83 additions & 0 deletions examples/android/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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(),
));
}
13 changes: 13 additions & 0 deletions src/graphics/vulkan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ pub fn initialize_xr_graphics(

let xr_entry = super::xr_entry();

#[cfg(target_os = "android")]
xr_entry.initialize_android_loader().unwrap();

let available_extensions = xr_entry.enumerate_extensions()?;
assert!(available_extensions.khr_vulkan_enable2);
info!("available xr exts: {:#?}", available_extensions);
Expand Down Expand Up @@ -82,8 +85,16 @@ pub fn initialize_xr_graphics(

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);
#[cfg(not(target_os = "android"))]
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)?;
if vk_target_version_xr < reqs.min_api_version_supported
|| vk_target_version_xr.major() > reqs.max_api_version_supported.major()
Expand All @@ -102,6 +113,8 @@ pub fn initialize_xr_graphics(
let device_extensions = vec![
ash::extensions::khr::Swapchain::name(),
ash::extensions::khr::DrawIndirectCount::name(),
#[cfg(target_os = "android")]
ash::extensions::khr::TimelineSemaphore::name(),
];
info!(
"creating vulkan instance with these extensions: {:#?}",
Expand Down
7 changes: 7 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,17 @@ impl PluginGroup for DefaultXrPlugins {
.add_before::<RenderPlugin, _>(OpenXrPlugin)
.add_after::<OpenXrPlugin, _>(OpenXrInput::new(XrControllerType::OculusTouch))
.set(WindowPlugin {
#[cfg(not(target_os = "android"))]
primary_window: Some(Window {
present_mode: PresentMode::AutoNoVsync,
..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()
})
}
Expand Down

0 comments on commit 879c40f

Please sign in to comment.