From febb8feb0be76d8ee015d09e6907700accf78f23 Mon Sep 17 00:00:00 2001 From: Patrick Towles Date: Wed, 14 Dec 2022 23:05:54 -0500 Subject: [PATCH] winit update and better support for android resume suspend --- Cargo.toml | 5 ++ crates/bevy_asset/Cargo.toml | 4 +- crates/bevy_asset/src/io/android_asset_io.rs | 12 ++- crates/bevy_asset/src/lib.rs | 14 +++- crates/bevy_derive/src/bevy_main.rs | 6 -- crates/bevy_internal/Cargo.toml | 6 +- crates/bevy_internal/src/lib.rs | 3 - crates/bevy_window/src/lib.rs | 4 +- crates/bevy_window/src/window.rs | 54 ++++++++++--- crates/bevy_winit/Cargo.toml | 4 + crates/bevy_winit/src/android.rs | 11 +++ crates/bevy_winit/src/converters.rs | 11 ++- crates/bevy_winit/src/lib.rs | 46 ++++++++--- crates/bevy_winit/src/winit_windows.rs | 4 +- examples/android/Cargo.toml | 41 ++++++++-- examples/android/build.rs | 15 ++++ examples/android/src/lib.rs | 85 +++++++++++++++++--- examples/android/src/main.rs | 6 ++ examples/asset/custom_asset_io.rs | 2 +- examples/ui/window_fallthrough.rs | 4 +- examples/window/window_settings.rs | 25 +++--- 21 files changed, 278 insertions(+), 84 deletions(-) create mode 100644 crates/bevy_winit/src/android.rs create mode 100644 examples/android/build.rs create mode 100644 examples/android/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index aa2eb4571a7fd..b70279beb7d52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -98,6 +98,7 @@ serialize = ["bevy_internal/serialize"] # Display server protocol support (X11 is enabled by default) wayland = ["bevy_internal/wayland"] x11 = ["bevy_internal/x11"] +android = ["bevy_internal/android"] # Enable rendering of font glyphs using subpixel accuracy subpixel_glyph_atlas = ["bevy_internal/subpixel_glyph_atlas"] @@ -130,6 +131,10 @@ bytemuck = "1.7" futures-lite = "1.11.3" crossbeam-channel = "0.5.0" +# TODO: Move these +[patch.crates-io] +winit = { git = "https://github.com/rust-windowing/winit", branch = "master" } + [[example]] name = "hello_world" path = "examples/hello_world.rs" diff --git a/crates/bevy_asset/Cargo.toml b/crates/bevy_asset/Cargo.toml index c64bb610bf939..84d5281e90344 100644 --- a/crates/bevy_asset/Cargo.toml +++ b/crates/bevy_asset/Cargo.toml @@ -22,6 +22,7 @@ bevy_log = { path = "../bevy_log", version = "0.9.0" } bevy_reflect = { path = "../bevy_reflect", version = "0.9.0", features = ["bevy"] } bevy_tasks = { path = "../bevy_tasks", version = "0.9.0" } bevy_utils = { path = "../bevy_utils", version = "0.9.0" } +bevy_winit = { path = "../bevy_winit", version = "0.9.0" } # other serde = { version = "1", features = ["derive"] } @@ -39,9 +40,6 @@ web-sys = { version = "0.3", features = ["Request", "Window", "Response"] } wasm-bindgen-futures = "0.4" js-sys = "0.3" -[target.'cfg(target_os = "android")'.dependencies] -ndk-glue = { version = "0.7" } - [dev-dependencies] futures-lite = "1.4.0" tempfile = "3.2.0" diff --git a/crates/bevy_asset/src/io/android_asset_io.rs b/crates/bevy_asset/src/io/android_asset_io.rs index 4b5f41b140fc2..37857cd196de6 100644 --- a/crates/bevy_asset/src/io/android_asset_io.rs +++ b/crates/bevy_asset/src/io/android_asset_io.rs @@ -1,6 +1,7 @@ use crate::{AssetIo, AssetIoError, Metadata}; use anyhow::Result; use bevy_utils::BoxedFuture; +use bevy_winit::android::AssetManager; use std::{ convert::TryFrom, ffi::CString, @@ -19,12 +20,17 @@ use std::{ /// [AssetManager]: https://developer.android.com/reference/android/content/res/AssetManager pub struct AndroidAssetIo { root_path: PathBuf, + asset_manager: AssetManager, } impl AndroidAssetIo { - pub fn new>(path: P) -> Self { + /// Creates a new `AndroidAssetIo` from apk + /// + /// See `get_base_path` below. + pub fn new>(path: P, asset_manager: AssetManager) -> Self { AndroidAssetIo { root_path: path.as_ref().to_owned(), + asset_manager, } } } @@ -32,8 +38,8 @@ impl AndroidAssetIo { impl AssetIo for AndroidAssetIo { fn load_path<'a>(&'a self, path: &'a Path) -> BoxedFuture<'a, Result, AssetIoError>> { Box::pin(async move { - let asset_manager = ndk_glue::native_activity().asset_manager(); - let mut opened_asset = asset_manager + let mut opened_asset = self + .asset_manager .open(&CString::new(path.to_str().unwrap()).unwrap()) .ok_or(AssetIoError::NotFound(path.to_path_buf()))?; let bytes = opened_asset.get_buffer()?; diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index d0535fa6d2102..78a466b0774ae 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -85,13 +85,20 @@ impl AssetPlugin { /// /// This is useful when providing a custom `AssetIo` instance that needs to /// delegate to the default `AssetIo` for the platform. - pub fn create_platform_default_asset_io(&self) -> Box { + #[allow(unused_variables)] + pub fn create_platform_default_asset_io(&self, app: &App) -> Box { #[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))] let source = FileAssetIo::new(&self.asset_folder, self.watch_for_changes); #[cfg(target_arch = "wasm32")] let source = WasmAssetIo::new(&self.asset_folder); #[cfg(target_os = "android")] - let source = AndroidAssetIo::new(&self.asset_folder); + let source = AndroidAssetIo::new( + &self.asset_folder, + app.world + .resource::() + .android_app + .asset_manager(), + ); Box::new(source) } @@ -100,7 +107,8 @@ impl AssetPlugin { impl Plugin for AssetPlugin { fn build(&self, app: &mut App) { if !app.world.contains_resource::() { - let source = self.create_platform_default_asset_io(); + let source = self.create_platform_default_asset_io(app); + let asset_server = AssetServer::with_boxed_io(source); app.insert_resource(asset_server); } diff --git a/crates/bevy_derive/src/bevy_main.rs b/crates/bevy_derive/src/bevy_main.rs index 6ec1363097f25..2055bb5a3f553 100644 --- a/crates/bevy_derive/src/bevy_main.rs +++ b/crates/bevy_derive/src/bevy_main.rs @@ -10,12 +10,6 @@ pub fn bevy_main(_attr: TokenStream, item: TokenStream) -> TokenStream { ); TokenStream::from(quote! { - // use ndk-glue macro to create an activity: https://github.com/rust-windowing/android-ndk-rs/tree/master/ndk-macro - #[cfg(target_os = "android")] - #[cfg_attr(target_os = "android", bevy::ndk_glue::main(backtrace = "on", ndk_glue = "bevy::ndk_glue"))] - fn android_main() { - main() - } #[no_mangle] #[cfg(target_os = "ios")] diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index ac371a3d4a82a..a08cf5f2fd2a8 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -51,6 +51,7 @@ serialize = ["bevy_core/serialize", "bevy_input/serialize", "bevy_time/serialize # Display server protocol support (X11 is enabled by default) wayland = ["bevy_winit/wayland"] x11 = ["bevy_winit/x11"] +android = ["bevy_winit/android"] # enable rendering of font glyphs using subpixel accuracy subpixel_glyph_atlas = ["bevy_text/subpixel_glyph_atlas"] @@ -97,8 +98,3 @@ bevy_text = { path = "../bevy_text", optional = true, version = "0.9.0" } bevy_ui = { path = "../bevy_ui", optional = true, version = "0.9.0" } bevy_winit = { path = "../bevy_winit", optional = true, version = "0.9.0" } bevy_gilrs = { path = "../bevy_gilrs", optional = true, version = "0.9.0" } - -[target.'cfg(target_os = "android")'.dependencies] -# This version *must* be the same as the version used by winit, -# or Android will break: https://github.com/rust-windowing/winit#android -ndk-glue = {version = "0.7", features = ["logger"]} diff --git a/crates/bevy_internal/src/lib.rs b/crates/bevy_internal/src/lib.rs index 90482b3e3615b..889ec628367f7 100644 --- a/crates/bevy_internal/src/lib.rs +++ b/crates/bevy_internal/src/lib.rs @@ -172,6 +172,3 @@ pub mod dynamic_plugin { //! Dynamic linking of plugins pub use bevy_dynamic_plugin::*; } - -#[cfg(target_os = "android")] -pub use ndk_glue; diff --git a/crates/bevy_window/src/lib.rs b/crates/bevy_window/src/lib.rs index 1632c06042d66..c06dcb15de29d 100644 --- a/crates/bevy_window/src/lib.rs +++ b/crates/bevy_window/src/lib.rs @@ -17,8 +17,8 @@ pub mod prelude { #[doc(hidden)] pub use crate::{ CursorEntered, CursorIcon, CursorLeft, CursorMoved, FileDragAndDrop, MonitorSelection, - ReceivedCharacter, Window, WindowDescriptor, WindowMode, WindowMoved, WindowPlugin, - WindowPosition, Windows, + ReceivedCharacter, Window, WindowDescriptor, WindowLevel, WindowMode, WindowMoved, + WindowPlugin, WindowPosition, Windows, }; } diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 17b54b508a59c..e48ca548f4cb0 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -68,6 +68,34 @@ pub enum PresentMode { Fifo = 4, // NOTE: The explicit ordinal values mirror wgpu. } +/// A window level groups windows with respect to their z-position. +/// +/// The relative ordering between windows in different window levels is fixed. +/// The z-order of a window within the same window level may change dynamically on user interaction. +/// +/// ## Platform-specific +/// +/// - **iOS / Android / Web / Wayland:** Unsupported. +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, FromReflect)] +#[cfg_attr( + feature = "serialize", + derive(serde::Serialize, serde::Deserialize), + reflect(Serialize, Deserialize) +)] +#[reflect(Debug, PartialEq, Hash)] +pub enum WindowLevel { + /// The default. + Normal = 0, + /// The window will always be below normal windows. + /// + /// This is useful for a widget-based app. + AlwaysOnBottom = 1, + + /// The window will always be on top of normal windows. + AlwaysOnTop = 2, +} + /// Specifies how the alpha channel of the textures should be handled during compositing. #[repr(C)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, FromReflect)] @@ -317,7 +345,7 @@ pub struct Window { fit_canvas_to_parent: bool, command_queue: Vec, alpha_mode: CompositeAlphaMode, - always_on_top: bool, + window_level: WindowLevel, } /// A command to be sent to a window. /// @@ -397,9 +425,9 @@ pub enum WindowCommand { SetResizeConstraints { resize_constraints: WindowResizeConstraints, }, - /// Set whether the window is always on top. - SetAlwaysOnTop { - always_on_top: bool, + /// Set whether the window level + SetWindowLevel { + window_level: WindowLevel, }, Close, } @@ -481,7 +509,7 @@ impl Window { fit_canvas_to_parent: window_descriptor.fit_canvas_to_parent, command_queue: Vec::new(), alpha_mode: window_descriptor.alpha_mode, - always_on_top: window_descriptor.always_on_top, + window_level: window_descriptor.window_level, } } /// Get the window's [`WindowId`]. @@ -861,15 +889,15 @@ impl Window { } /// Get whether or not the window is always on top. #[inline] - pub fn always_on_top(&self) -> bool { - self.always_on_top + pub fn window_level(&self) -> WindowLevel { + self.window_level } /// Set whether of not the window is always on top. - pub fn set_always_on_top(&mut self, always_on_top: bool) { - self.always_on_top = always_on_top; + pub fn set_window_level(&mut self, window_level: WindowLevel) { + self.window_level = window_level; self.command_queue - .push(WindowCommand::SetAlwaysOnTop { always_on_top }); + .push(WindowCommand::SetWindowLevel { window_level }); } /// Close the operating system window corresponding to this [`Window`]. /// @@ -1064,12 +1092,12 @@ pub struct WindowDescriptor { pub fit_canvas_to_parent: bool, /// Specifies how the alpha channel of the textures should be handled during compositing. pub alpha_mode: CompositeAlphaMode, - /// Sets the window to always be on top of other windows. + /// Sets the window level /// /// ## Platform-specific /// - iOS / Android / Web: Unsupported. /// - Linux (Wayland): Unsupported. - pub always_on_top: bool, + pub window_level: WindowLevel, } impl Default for WindowDescriptor { @@ -1093,7 +1121,7 @@ impl Default for WindowDescriptor { canvas: None, fit_canvas_to_parent: false, alpha_mode: CompositeAlphaMode::Auto, - always_on_top: false, + window_level: WindowLevel::Normal, } } } diff --git a/crates/bevy_winit/Cargo.toml b/crates/bevy_winit/Cargo.toml index 96864623f1113..66cd507048da9 100644 --- a/crates/bevy_winit/Cargo.toml +++ b/crates/bevy_winit/Cargo.toml @@ -12,6 +12,7 @@ keywords = ["bevy"] trace = [] wayland = ["winit/wayland"] x11 = ["winit/x11"] +android = ["winit/android-native-activity"] [dependencies] # bevy @@ -27,6 +28,9 @@ winit = { version = "0.27", default-features = false } approx = { version = "0.5", default-features = false } raw-window-handle = "0.5" +[target.'cfg(target_os = "android")'.dependencies] +ndk = "0.7" # Need till https://github.com/rib/android-activity/issues/45 + [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen = { version = "0.2" } web-sys = "0.3" diff --git a/crates/bevy_winit/src/android.rs b/crates/bevy_winit/src/android.rs new file mode 100644 index 0000000000000..1a208ecb5c61b --- /dev/null +++ b/crates/bevy_winit/src/android.rs @@ -0,0 +1,11 @@ +use bevy_ecs::system::Resource; + +pub use ndk::asset::AssetManager; +pub use winit::platform::android::activity::*; + +/// Resource containing AndroidApp +pub struct AndroidActivityApp { + pub android_app: AndroidApp, +} + +impl Resource for AndroidActivityApp {} diff --git a/crates/bevy_winit/src/converters.rs b/crates/bevy_winit/src/converters.rs index 1702d2db96af3..c5bece8cbf401 100644 --- a/crates/bevy_winit/src/converters.rs +++ b/crates/bevy_winit/src/converters.rs @@ -5,7 +5,7 @@ use bevy_input::{ ButtonState, }; use bevy_math::Vec2; -use bevy_window::{CursorGrabMode, CursorIcon}; +use bevy_window::{CursorGrabMode, CursorIcon, WindowLevel}; pub fn convert_keyboard_input(keyboard_input: &winit::event::KeyboardInput) -> KeyboardInput { KeyboardInput { @@ -275,3 +275,12 @@ pub fn convert_cursor_grab_mode(mode: CursorGrabMode) -> winit::window::CursorGr CursorGrabMode::Locked => winit::window::CursorGrabMode::Locked, } } + +/// Map [`bevy_window::WindowLevel`] to [`winit::window::WindowLevel`]. +pub fn convert_window_level(level: WindowLevel) -> winit::window::WindowLevel { + match level { + WindowLevel::Normal => winit::window::WindowLevel::Normal, + WindowLevel::AlwaysOnTop => winit::window::WindowLevel::AlwaysOnTop, + WindowLevel::AlwaysOnBottom => winit::window::WindowLevel::AlwaysOnBottom, + } +} diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 749d256882500..eec6c08d5a933 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -1,9 +1,15 @@ +#[cfg(target_os = "android")] +pub mod android; mod converters; #[cfg(target_arch = "wasm32")] mod web_resize; + mod winit_config; mod winit_windows; +use converters::convert_window_level; +use winit::event_loop::EventLoopBuilder; + use winit::window::CursorGrabMode; pub use winit_config::*; pub use winit_windows::*; @@ -44,7 +50,17 @@ impl Plugin for WinitPlugin { .add_system_to_stage(CoreStage::PostUpdate, change_window.label(ModifiesWindows)); #[cfg(target_arch = "wasm32")] app.add_plugin(web_resize::CanvasParentResizePlugin); - let event_loop = EventLoop::new(); + + // Build event loop + let mut event_loop_builder = EventLoopBuilder::new(); + #[cfg(target_os = "android")] + { + use winit::platform::android::EventLoopBuilderExtAndroid; + let android_resource = app.world.resource::(); + event_loop_builder.with_android_app(android_resource.android_app.clone()); + } + let event_loop = event_loop_builder.build(); + #[cfg(not(any(target_os = "android", target_os = "ios", target_os = "macos")))] let mut create_window_reader = WinitCreateWindowReader::default(); #[cfg(any(target_os = "android", target_os = "ios", target_os = "macos"))] @@ -235,9 +251,9 @@ fn change_window( window.set_max_inner_size(Some(max_inner_size)); } } - bevy_window::WindowCommand::SetAlwaysOnTop { always_on_top } => { + bevy_window::WindowCommand::SetWindowLevel { window_level } => { let window = winit_windows.get_window(id).unwrap(); - window.set_always_on_top(always_on_top); + window.set_window_level(convert_window_level(window_level)); } bevy_window::WindowCommand::SetCursorHitTest { hittest } => { let window = winit_windows.get_window(id).unwrap(); @@ -338,7 +354,11 @@ struct WinitPersistentState { impl Default for WinitPersistentState { fn default() -> Self { Self { + #[cfg(not(target_os = "android"))] active: true, + // Note: Could be false for ever os, since winit sends a resume event on start for every os now + #[cfg(target_os = "android")] + active: false, low_power_event: false, redraw_request_sent: false, timeout_reached: false, @@ -580,18 +600,26 @@ pub fn winit_runner_with(mut app: App) { } event::Event::Suspended => { winit_state.active = false; + #[cfg(target_os = "android")] + { + // Bevy doesnt support suspend/resume so we just exit + // and android will restart the bevy on resume + // TODO: Save save some state and load on resume + *control_flow = ControlFlow::Exit; + } } event::Event::Resumed => { winit_state.active = true; } event::Event::MainEventsCleared => { - handle_create_window_events( - &mut app.world, - event_loop, - &mut create_window_event_reader, - ); - let winit_config = app.world.resource::(); let update = if winit_state.active { + handle_create_window_events( + &mut app.world, + event_loop, + &mut create_window_event_reader, + ); + + let winit_config = app.world.resource::(); let windows = app.world.resource::(); let focused = windows.iter().any(|w| w.is_focused()); match winit_config.update_mode(focused) { diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index 3364c850b3c28..2953a0ae983e6 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -1,4 +1,4 @@ -use crate::converters::convert_cursor_grab_mode; +use crate::converters::{convert_cursor_grab_mode, convert_window_level}; use bevy_math::{DVec2, IVec2}; use bevy_utils::HashMap; use bevy_window::{ @@ -73,7 +73,7 @@ impl WinitWindows { .with_resizable(window_descriptor.resizable) .with_decorations(window_descriptor.decorations) .with_transparent(window_descriptor.transparent) - .with_always_on_top(window_descriptor.always_on_top), + .with_window_level(convert_window_level(window_descriptor.window_level)), }; let constraints = window_descriptor.resize_constraints.check_constraints(); diff --git a/examples/android/Cargo.toml b/examples/android/Cargo.toml index 6f8cd0074a7bf..bcb0089d21ff0 100644 --- a/examples/android/Cargo.toml +++ b/examples/android/Cargo.toml @@ -2,28 +2,53 @@ name = "bevy-android-example" version = "0.1.0" edition = "2021" -description = "Example for building an Android app with Bevy" +description = "Example for building an Android app with Bevy and Native Activity" publish = false license = "MIT OR Apache-2.0" +[features] +default = ["bevy/android"] +desktop = ["bevy/x11"] + +[[bin]] +name = "bevy-android-example" +path = "src/main.rs" +required-features = ["desktop"] + [lib] -name = "bevy_android_example" -crate-type = ["cdylib"] +crate-type = ["lib", "cdylib"] [dependencies] -bevy = { path = "../../" } +bevy = { path = "../../", features = [ + "animation", + "bevy_asset", + "bevy_audio", + #"bevy_gilrs", Gilrs does not support android + "bevy_scene", + "bevy_winit", + "bevy_core_pipeline", + "bevy_pbr", + "bevy_gltf", + "bevy_render", + "bevy_sprite", + "bevy_text", + "bevy_ui", + "png", + "hdr", + "vorbis", + "filesystem_watcher", +], default-features = false } [package.metadata.android] package = "org.bevyengine.example" apk_name = "bevyexample" assets = "../../assets" resources = "../../assets/android-res" -build_targets = ["aarch64-linux-android", "armv7-linux-androideabi"] +build_targets = ["aarch64-linux-android", "armv7-linux-androideabi", "x86_64-linux-android"] [package.metadata.android.sdk] -target_sdk_version = 31 +target_sdk_version = 33 [package.metadata.android.application] icon = "@mipmap/ic_launcher" -label = "Bevy Example" - +label = "Bevy Example" \ No newline at end of file diff --git a/examples/android/build.rs b/examples/android/build.rs new file mode 100644 index 0000000000000..5bcfa746e03ca --- /dev/null +++ b/examples/android/build.rs @@ -0,0 +1,15 @@ +use std::env; + +fn main() { + // Note: bevy_audio fix + // This would not be needed if using cpal directly, but bevy_audio uses + // rodio which uses cpal, and its not clear why, but c++ lib is not properly + // linked, this is a workaround for now. This can also be fixed by enabling + // the shared-stdcxx feature for oboe in cpal + // https://github.com/RustAudio/cpal/issues/720 + // https://github.com/RustAudio/cpal/issues/563 + let target_os = env::var("CARGO_CFG_TARGET_OS"); + if let Ok("android") = target_os.as_ref().map(|x| &**x) { + println!("cargo:rustc-link-lib=c++_shared"); + } +} diff --git a/examples/android/src/lib.rs b/examples/android/src/lib.rs index 6db1cae703397..8f43dd2a897f6 100644 --- a/examples/android/src/lib.rs +++ b/examples/android/src/lib.rs @@ -1,21 +1,52 @@ use bevy::{ + log::{Level, LogPlugin}, prelude::*, - render::settings::{WgpuSettings, WgpuSettingsPriority}, }; -// the `bevy_main` proc_macro generates the required android boilerplate -#[bevy_main] -fn main() { - App::new() +#[no_mangle] +#[cfg(target_os = "android")] +fn android_main(android_app: bevy::winit::android::AndroidApp) { + let mut app = App::new(); + app.insert_resource(bevy::winit::android::AndroidActivityApp { android_app }); + main(&mut app) +} + +pub fn main(app: &mut App) { + #[cfg(target_os = "android")] + { + use bevy::render::settings::{WgpuLimits, WgpuSettings, WgpuSettingsPriority}; + // This configures the app to use the most compatible rendering settings. // They help with compatibility with as many devices as possible. - .insert_resource(WgpuSettings { + app.insert_resource(WgpuSettings { priority: WgpuSettingsPriority::Compatibility, + limits: WgpuLimits { + // Was required for my device and emulator + max_storage_textures_per_shader_stage: 4, + ..default() + }, ..default() - }) - .add_plugins(DefaultPlugins) - .add_startup_system(setup) - .run(); + }); + } + + app.add_plugins( + DefaultPlugins + .set(LogPlugin { + filter: "android_activity=warn,wgpu=warn".to_string(), + level: Level::INFO, + }) + .set(AssetPlugin { + asset_folder: if cfg!(target_os = "android") { + "assets".to_string() + } else { + "../../assets".to_string() + }, + ..default() + }), + ) + .add_startup_system(setup) + .add_system(rotate_camera) + .run(); } /// set up a simple 3D scene @@ -23,6 +54,8 @@ fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, + asset_server: Res, + audio: Res