This commit is contained in:
Schmarni
2024-01-23 05:45:48 +01:00
parent 87fe9093df
commit 6f6948e627
9 changed files with 264 additions and 630 deletions

View File

@@ -13,6 +13,7 @@ android:
# required: true # required: true
uses_permission: uses_permission:
- name: "com.oculus.permission.HAND_TRACKING" - name: "com.oculus.permission.HAND_TRACKING"
- name: "android.permission.INTERNET"
application: application:
label: "Bevy Openxr Android" label: "Bevy Openxr Android"
theme: "@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen" theme: "@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen"

View File

@@ -19,7 +19,7 @@ use bevy_oxr::{
graphics::{extensions::XrExtensions, XrAppInfo, XrPreferdBlendMode}, graphics::{extensions::XrExtensions, XrAppInfo, XrPreferdBlendMode},
input::XrInput, input::XrInput,
resources::{XrFrameState, XrInstance, XrSession}, resources::{XrFrameState, XrInstance, XrSession},
xr_init::{xr_only, XrEnableRequest, XrEnableStatus}, xr_init::{xr_only, XrStatus},
xr_input::{ xr_input::{
actions::XrActionSets, actions::XrActionSets,
debug_gizmos::OpenXrDebugRenderer, debug_gizmos::OpenXrDebugRenderer,
@@ -41,18 +41,18 @@ use bevy_oxr::{
DefaultXrPlugins, DefaultXrPlugins,
}; };
fn input_stuff( // fn input_stuff(
keys: Res<Input<KeyCode>>, // keys: Res<Input<KeyCode>>,
status: Res<XrEnableStatus>, // status: Res<XrEnableStatus>,
mut request: EventWriter<XrEnableRequest>, // mut request: EventWriter<XrEnableRequest>,
) { // ) {
if keys.just_pressed(KeyCode::Space) { // if keys.just_pressed(KeyCode::Space) {
match status.into_inner() { // match status.into_inner() {
XrEnableStatus::Enabled => request.send(XrEnableRequest::TryDisable), // XrEnableStatus::Enabled => request.send(XrEnableRequest::TryDisable),
XrEnableStatus::Disabled => request.send(XrEnableRequest::TryEnable), // XrEnableStatus::Disabled => request.send(XrEnableRequest::TryEnable),
} // }
} // }
} // }
mod setup; mod setup;
use crate::setup::setup_scene; use crate::setup::setup_scene;
@@ -66,7 +66,7 @@ pub fn main() {
let mut app = App::new(); let mut app = App::new();
let mut xr_extensions = XrExtensions::default(); let mut xr_extensions = XrExtensions::default();
app.add_systems(Update, input_stuff) app
//lets get the usual diagnostic stuff added //lets get the usual diagnostic stuff added
.add_plugins(LogDiagnosticsPlugin::default()) .add_plugins(LogDiagnosticsPlugin::default())
.add_plugins(FrameTimeDiagnosticsPlugin) .add_plugins(FrameTimeDiagnosticsPlugin)

View File

@@ -103,7 +103,7 @@ pub fn try_full_init(
)> { )> {
let mut system_state: SystemState<Query<&RawHandleWrapper, With<PrimaryWindow>>> = let mut system_state: SystemState<Query<&RawHandleWrapper, With<PrimaryWindow>>> =
SystemState::new(world); SystemState::new(world);
let primary_window = system_state.get(&world).get_single().ok().cloned(); let primary_window = system_state.get(world).get_single().ok().cloned();
let ( let (
xr_instance, xr_instance,
setup_info, setup_info,

View File

@@ -6,10 +6,13 @@ pub mod resources;
pub mod xr_init; pub mod xr_init;
pub mod xr_input; pub mod xr_input;
use std::fs::File;
use std::io::{BufWriter, Write};
use std::net::TcpStream;
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use crate::xr_init::RenderRestartPlugin; use crate::xr_init::{StartXrSession, XrInitPlugin};
use crate::xr_input::hands::hand_tracking::DisableHandTracking; use crate::xr_input::hands::hand_tracking::DisableHandTracking;
use crate::xr_input::oculus_touch::ActionSets; use crate::xr_input::oculus_touch::ActionSets;
use bevy::app::{AppExit, PluginGroupBuilder}; use bevy::app::{AppExit, PluginGroupBuilder};
@@ -18,22 +21,25 @@ use bevy::prelude::*;
use bevy::render::camera::{ManualTextureView, ManualTextureViewHandle, ManualTextureViews}; use bevy::render::camera::{ManualTextureView, ManualTextureViewHandle, ManualTextureViews};
use bevy::render::extract_resource::ExtractResourcePlugin; use bevy::render::extract_resource::ExtractResourcePlugin;
use bevy::render::pipelined_rendering::PipelinedRenderingPlugin; use bevy::render::pipelined_rendering::PipelinedRenderingPlugin;
use bevy::render::renderer::{render_system, RenderInstance}; use bevy::render::renderer::RenderInstance;
use bevy::render::settings::RenderCreation; use bevy::render::settings::RenderCreation;
use bevy::render::{Render, RenderApp, RenderPlugin, RenderSet}; use bevy::render::{Render, RenderApp, RenderPlugin, RenderSet};
use bevy::window::{PresentMode, PrimaryWindow, RawHandleWrapper}; use bevy::window::{PresentMode, PrimaryWindow, RawHandleWrapper};
use eyre::anyhow;
use graphics::extensions::XrExtensions; use graphics::extensions::XrExtensions;
use graphics::{XrAppInfo, XrPreferdBlendMode}; use graphics::{XrAppInfo, XrPreferdBlendMode};
use input::XrInput; use input::XrInput;
use openxr as xr; use openxr as xr;
// use passthrough::{start_passthrough, supports_passthrough, XrPassthroughLayer}; // use passthrough::{start_passthrough, supports_passthrough, XrPassthroughLayer};
use resources::*; use resources::*;
use xr::{FormFactor, FrameWaiter}; use xr::FormFactor;
use xr_init::{xr_only, XrEnableStatus, XrRenderData}; use xr_init::{
xr_only, xr_render_only, CleanupXrData, XrEarlyInitPlugin, XrShouldRender, XrStatus,
};
use xr_input::controllers::XrControllerType; use xr_input::controllers::XrControllerType;
use xr_input::hands::emulated::HandEmulationPlugin; use xr_input::hands::emulated::HandEmulationPlugin;
use xr_input::hands::hand_tracking::{HandTrackingData, HandTrackingPlugin}; use xr_input::hands::hand_tracking::{HandTrackingData, HandTrackingPlugin};
use xr_input::hands::XrHandPlugins;
use xr_input::xr_camera::XrCameraType;
use xr_input::OpenXrInput; use xr_input::OpenXrInput;
const VIEW_TYPE: xr::ViewConfigurationType = xr::ViewConfigurationType::PRIMARY_STEREO; const VIEW_TYPE: xr::ViewConfigurationType = xr::ViewConfigurationType::PRIMARY_STEREO;
@@ -49,141 +55,119 @@ pub struct OpenXrPlugin {
app_info: XrAppInfo, app_info: XrAppInfo,
} }
#[derive(Resource)]
pub struct FutureXrResources(
pub Arc<
Mutex<
Option<(
XrInstance,
XrSession,
XrEnvironmentBlendMode,
XrResolution,
XrFormat,
XrSessionRunning,
XrSwapchain,
XrInput,
XrViews,
XrFrameState,
)>,
>,
>,
);
impl Plugin for OpenXrPlugin { impl Plugin for OpenXrPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.insert_resource(XrSessionRunning::new(AtomicBool::new(false))); app.insert_resource(XrSessionRunning::new(AtomicBool::new(false)));
// #[cfg(target_os = "android")]
// {
// let ctx = ndk_context::android_context();
// let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }.unwrap();
// let env = vm.attach_current_thread_permanently();
// }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
match graphics::try_full_init( match graphics::initialize_xr_instance(
&mut app.world, SystemState::<Query<&RawHandleWrapper, With<PrimaryWindow>>>::new(&mut app.world)
.get(&app.world)
.get_single()
.ok()
.cloned(),
self.reqeusted_extensions.clone(), self.reqeusted_extensions.clone(),
self.prefered_blend_mode, self.prefered_blend_mode,
self.app_info.clone(), self.app_info.clone(),
) { ) {
Ok((device, queue, adapter_info, render_adapter, instance)) => { Ok((
xr_instance,
oxr_session_setup_info,
blend_mode,
device,
queue,
adapter_info,
render_adapter,
instance,
)) => {
debug!("Configured wgpu adapter Limits: {:#?}", device.limits()); debug!("Configured wgpu adapter Limits: {:#?}", device.limits());
debug!("Configured wgpu adapter Features: {:#?}", device.features()); debug!("Configured wgpu adapter Features: {:#?}", device.features());
warn!("Starting in Xr"); warn!("Starting with OpenXR Instance");
app.insert_resource(ActionSets(vec![])); app.insert_resource(ActionSets(vec![]));
app.insert_resource(xr_instance);
app.insert_resource(blend_mode);
app.insert_non_send_resource(oxr_session_setup_info);
let render_instance = RenderInstance(instance.into());
app.insert_resource(render_instance.clone());
app.add_plugins(RenderPlugin { app.add_plugins(RenderPlugin {
render_creation: RenderCreation::Manual( render_creation: RenderCreation::Manual(
device, device,
queue, queue,
adapter_info, adapter_info,
render_adapter, render_adapter,
instance, render_instance,
), ),
}); });
app.insert_resource(XrEnableStatus::Disabled); app.insert_resource(XrStatus::Disabled);
app.world.send_event(StartXrSession);
} }
Err(err) => { Err(err) => {
warn!("OpenXR Failed to initialize: {}", err); warn!("OpenXR Instance Failed to initialize: {}", err);
app.add_plugins(RenderPlugin::default()); app.add_plugins(RenderPlugin::default());
app.add_plugins(ExtractResourcePlugin::<XrEnableStatus>::default()); app.insert_resource(XrStatus::NoInstance);
app.insert_resource(XrEnableStatus::Disabled);
} }
} }
// app.add_systems(PreUpdate, mr_test);
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
{ {
app.add_plugins(RenderPlugin::default()); app.add_plugins(RenderPlugin::default());
app.insert_resource(XrEnableStatus::Disabled); app.insert_resource(XrStatus::Disabled);
} }
app.add_plugins(ExtractResourcePlugin::<XrEnableStatus>::default());
app.add_systems( app.add_systems(
PreUpdate, PreUpdate,
(xr_poll_events, xr_begin_frame.run_if(xr_only())).chain(), xr_poll_events.run_if(|status: Res<XrStatus>| *status != XrStatus::NoInstance),
); );
} app.add_systems(
PreUpdate,
fn finish(&self, app: &mut App) { (
// TODO: Split this up into the indevidual resources xr_reset_should_render,
// app.world.get_resource::<XrEnableStatus>() == Some(&XrEnableStatus::Enabled) apply_deferred,
if true { xr_wait_frame.run_if(xr_only()),
warn!("finished xr init"); apply_deferred,
let xr_instance = app locate_views.run_if(xr_only()),
.world apply_deferred,
.get_resource::<XrInstance>()
.expect("should exist");
let xr_session = app.world.get_resource::<XrSession>().expect("should exist");
let hands = xr_instance.exts().ext_hand_tracking.is_some()
&& xr_instance
.supports_hand_tracking(
xr_instance
.system(FormFactor::HEAD_MOUNTED_DISPLAY)
.unwrap(),
) )
.is_ok_and(|v| v); .chain()
if hands { .after(xr_poll_events),
app.insert_resource(HandTrackingData::new(xr_session).unwrap()); );
} else {
app.insert_resource(DisableHandTracking::Both);
}
let xr_swapchain = app
.world
.get_resource::<XrSwapchain>()
.expect("should exist");
let xr_resolution = app
.world
.get_resource::<XrResolution>()
.expect("should exist");
let xr_format = app.world.get_resource::<XrFormat>().expect("should exist");
let (left, right) = xr_swapchain.get_render_views();
let left = ManualTextureView {
texture_view: left.into(),
size: **xr_resolution,
format: **xr_format,
};
let right = ManualTextureView {
texture_view: right.into(),
size: **xr_resolution,
format: **xr_format,
};
let mut manual_texture_views = app.world.resource_mut::<ManualTextureViews>();
manual_texture_views.insert(LEFT_XR_TEXTURE_HANDLE, left);
manual_texture_views.insert(RIGHT_XR_TEXTURE_HANDLE, right);
drop(manual_texture_views);
let render_app = app.sub_app_mut(RenderApp); let render_app = app.sub_app_mut(RenderApp);
render_app.add_systems( render_app.add_systems(
Render, Render,
( xr_begin_frame
post_frame
.run_if(xr_only()) .run_if(xr_only())
.before(render_system) .run_if(xr_render_only())
.after(RenderSet::ExtractCommands), .after(RenderSet::ExtractCommands)
end_frame.run_if(xr_only()).after(render_system), .before(xr_pre_frame),
),
); );
} render_app.add_systems(
Render,
xr_pre_frame
.run_if(xr_only())
.run_if(xr_render_only())
.in_set(RenderSet::Prepare),
);
render_app.add_systems(
Render,
(locate_views, xr_input::xr_camera::xr_camera_head_sync)
.chain()
.run_if(xr_only())
.run_if(xr_render_only())
.in_set(RenderSet::Prepare),
);
render_app.add_systems(
Render,
xr_end_frame
.run_if(xr_only())
.run_if(xr_render_only())
.after(RenderSet::Render),
);
render_app.insert_resource(TcpConnection(
TcpStream::connect("192.168.2.100:6969").unwrap(),
));
} }
} }
#[derive(Resource)]
struct TcpConnection(TcpStream);
#[derive(Default)] #[derive(Default)]
pub struct DefaultXrPlugins { pub struct DefaultXrPlugins {
pub reqeusted_extensions: XrExtensions, pub reqeusted_extensions: XrExtensions,
@@ -203,11 +187,10 @@ impl PluginGroup for DefaultXrPlugins {
app_info: self.app_info.clone(), app_info: self.app_info.clone(),
}) })
.add_after::<OpenXrPlugin, _>(OpenXrInput::new(XrControllerType::OculusTouch)) .add_after::<OpenXrPlugin, _>(OpenXrInput::new(XrControllerType::OculusTouch))
.add_before::<OpenXrPlugin, _>(RenderRestartPlugin) .add_after::<OpenXrPlugin, _>(XrInitPlugin)
.add(HandEmulationPlugin) .add_before::<OpenXrPlugin, _>(XrEarlyInitPlugin)
.add(HandTrackingPlugin) .add(XrHandPlugins)
.add(XrResourcePlugin) .add(XrResourcePlugin)
// .add(xr_init::RenderRestartPlugin)
.set(WindowPlugin { .set(WindowPlugin {
#[cfg(not(target_os = "android"))] #[cfg(not(target_os = "android"))]
primary_window: Some(Window { primary_window: Some(Window {
@@ -227,11 +210,16 @@ impl PluginGroup for DefaultXrPlugins {
} }
} }
pub fn xr_poll_events( fn xr_reset_should_render(mut should: ResMut<XrShouldRender>) {
**should = false;
}
fn xr_poll_events(
instance: Option<Res<XrInstance>>, instance: Option<Res<XrInstance>>,
session: Option<Res<XrSession>>, session: Option<Res<XrSession>>,
session_running: Res<XrSessionRunning>, session_running: Res<XrSessionRunning>,
mut app_exit: EventWriter<AppExit>, mut app_exit: EventWriter<AppExit>,
mut cleanup_xr: EventWriter<CleanupXrData>,
) { ) {
if let (Some(instance), Some(session)) = (instance, session) { if let (Some(instance), Some(session)) = (instance, session) {
let _span = info_span!("xr_poll_events"); let _span = info_span!("xr_poll_events");
@@ -251,6 +239,7 @@ pub fn xr_poll_events(
xr::SessionState::STOPPING => { xr::SessionState::STOPPING => {
session.end().unwrap(); session.end().unwrap();
session_running.store(false, std::sync::atomic::Ordering::Relaxed); session_running.store(false, std::sync::atomic::Ordering::Relaxed);
cleanup_xr.send_default();
} }
xr::SessionState::EXITING | xr::SessionState::LOSS_PENDING => { xr::SessionState::EXITING | xr::SessionState::LOSS_PENDING => {
// app_exit.send(AppExit); // app_exit.send(AppExit);
@@ -260,7 +249,9 @@ pub fn xr_poll_events(
_ => {} _ => {}
} }
} }
InstanceLossPending(_) => return, InstanceLossPending(_) => {
app_exit.send_default();
}
EventsLost(e) => { EventsLost(e) => {
warn!("lost {} XR events", e.lost_event_count()); warn!("lost {} XR events", e.lost_event_count());
} }
@@ -270,13 +261,15 @@ pub fn xr_poll_events(
} }
} }
pub fn xr_begin_frame( fn xr_begin_frame(swapchain: Res<XrSwapchain>) {
session: Res<XrSession>, let _span = info_span!("xr_begin_frame").entered();
swapchain.begin().unwrap()
}
pub fn xr_wait_frame(
mut frame_state: ResMut<XrFrameState>, mut frame_state: ResMut<XrFrameState>,
mut frame_waiter: ResMut<XrFrameWaiter>, mut frame_waiter: ResMut<XrFrameWaiter>,
swapchain: Res<XrSwapchain>, mut should_render: ResMut<XrShouldRender>,
mut views: ResMut<XrViews>,
input: Res<XrInput>,
) { ) {
{ {
let _span = info_span!("xr_wait_frame").entered(); let _span = info_span!("xr_wait_frame").entered();
@@ -287,21 +280,11 @@ pub fn xr_begin_frame(
return; return;
} }
}; };
} **should_render = frame_state.should_render;
{
let _span = info_span!("xr_begin_frame").entered();
swapchain.begin().unwrap()
}
{
let _span = info_span!("xr_locate_views").entered();
**views = session
.locate_views(VIEW_TYPE, frame_state.predicted_display_time, &input.stage)
.unwrap()
.1;
} }
} }
pub fn post_frame( pub fn xr_pre_frame(
resolution: Res<XrResolution>, resolution: Res<XrResolution>,
format: Res<XrFormat>, format: Res<XrFormat>,
swapchain: Res<XrSwapchain>, swapchain: Res<XrSwapchain>,
@@ -313,7 +296,9 @@ pub fn post_frame(
} }
{ {
let _span = info_span!("xr_wait_image").entered(); let _span = info_span!("xr_wait_image").entered();
info!("wait image");
swapchain.wait_image().unwrap(); swapchain.wait_image().unwrap();
info!("waited image");
} }
{ {
let _span = info_span!("xr_update_manual_texture_views").entered(); let _span = info_span!("xr_update_manual_texture_views").entered();
@@ -333,36 +318,51 @@ pub fn post_frame(
} }
} }
pub fn end_frame( pub fn xr_end_frame(
xr_frame_state: Option<Res<XrFrameState>>, xr_frame_state: Res<XrFrameState>,
views: Option<Res<XrViews>>, views: Res<XrViews>,
input: Option<Res<XrInput>>, input: Res<XrInput>,
swapchain: Option<Res<XrSwapchain>>, swapchain: Res<XrSwapchain>,
resolution: Option<Res<XrResolution>>, resolution: Res<XrResolution>,
environment_blend_mode: Option<Res<XrEnvironmentBlendMode>>, environment_blend_mode: Res<XrEnvironmentBlendMode>,
// _main_thread: NonSend<()>, mut connection: ResMut<TcpConnection>,
#[cfg(target_os = "android")] mut attached: Local<bool>, cams: Query<(&Transform, &XrCameraType)>,
// passthrough_layer: Option<Res<XrPassthroughLayer>>,
) { ) {
let xr_frame_state = xr_frame_state.unwrap();
let views = views.unwrap();
let input = input.unwrap();
let swapchain = swapchain.unwrap();
let resolution = resolution.unwrap();
let environment_blend_mode = environment_blend_mode.unwrap();
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
// if !*attached {
{ {
let ctx = ndk_context::android_context(); let ctx = ndk_context::android_context();
let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }.unwrap(); let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }.unwrap();
let env = vm.attach_current_thread_as_daemon(); let env = vm.attach_current_thread_as_daemon();
*attached = true;
} }
{ {
let _span = info_span!("xr_release_image").entered(); let _span = info_span!("xr_release_image").entered();
swapchain.release_image().unwrap(); swapchain.release_image().unwrap();
} }
let mut cam = None;
for (t, c) in &cams {
if *c == XrCameraType::Xr(xr_input::xr_camera::Eye::Left) {
cam = Some(*t);
break;
}
}
let _ = std::writeln!(
&mut connection.0,
"{},{},{},{},{},{},{},{},{},{},{},{},{},{}",
views[0].pose.position.x,
views[0].pose.position.y,
views[0].pose.position.z,
views[0].pose.orientation.x,
views[0].pose.orientation.y,
views[0].pose.orientation.z,
views[0].pose.orientation.w,
cam.unwrap().translation.x,
cam.unwrap().translation.y,
cam.unwrap().translation.z,
cam.unwrap().rotation.x,
cam.unwrap().rotation.y,
cam.unwrap().rotation.z,
cam.unwrap().rotation.w,
);
{ {
let _span = info_span!("xr_end_frame").entered(); let _span = info_span!("xr_end_frame").entered();
let result = swapchain.end( let result = swapchain.end(

View File

@@ -5,7 +5,7 @@ use std::sync::Mutex;
use crate::input::XrInput; use crate::input::XrInput;
// use crate::passthrough::XrPassthroughLayer; // use crate::passthrough::XrPassthroughLayer;
use crate::resource_macros::*; use crate::resource_macros::*;
use crate::xr_init::XrEnableStatus; use crate::xr_init::XrStatus;
use bevy::prelude::*; use bevy::prelude::*;
use bevy::render::extract_resource::ExtractResourcePlugin; use bevy::render::extract_resource::ExtractResourcePlugin;
use openxr as xr; use openxr as xr;
@@ -27,7 +27,6 @@ pub(crate) struct VulkanOXrSessionSetupInfo {
pub(crate) vk_instance_ptr: *const c_void, pub(crate) vk_instance_ptr: *const c_void,
pub(crate) queue_family_index: u32, pub(crate) queue_family_index: u32,
pub(crate) xr_system_id: xr::SystemId, pub(crate) xr_system_id: xr::SystemId,
// pub(crate) swapchain_create_info: xr::SwapchainCreateInfo<xr::Vulkan>
} }
pub(crate) enum OXrSessionSetupInfo { pub(crate) enum OXrSessionSetupInfo {
@@ -46,6 +45,7 @@ impl Plugin for XrResourcePlugin {
app.add_plugins(ExtractResourcePlugin::<XrInput>::default()); app.add_plugins(ExtractResourcePlugin::<XrInput>::default());
app.add_plugins(ExtractResourcePlugin::<XrEnvironmentBlendMode>::default()); app.add_plugins(ExtractResourcePlugin::<XrEnvironmentBlendMode>::default());
app.add_plugins(ExtractResourcePlugin::<XrSessionRunning>::default()); app.add_plugins(ExtractResourcePlugin::<XrSessionRunning>::default());
app.add_plugins(ExtractResourcePlugin::<XrSession>::default());
} }
} }
@@ -219,7 +219,6 @@ impl<G: xr::Graphics> SwapchainInner<G> {
// } // }
// None => // None =>
info!("swapchain stream lock");
let r = self.stream.lock().unwrap().end( let r = self.stream.lock().unwrap().end(
predicted_display_time, predicted_display_time,
environment_blend_mode, environment_blend_mode,
@@ -244,7 +243,6 @@ impl<G: xr::Graphics> SwapchainInner<G> {
), ),
])], ])],
); );
info!("swapchain stream done");
r r
// } // }
} }

View File

@@ -1,433 +0,0 @@
// Just a lot of code that is meant for something way more complex but hey.
// maybe will work on that soon
use std::sync::Arc;
use bevy::{
ecs::schedule::{ExecutorKind, ScheduleLabel},
prelude::*,
render::{
extract_resource::{ExtractResource, ExtractResourcePlugin},
renderer::{
self, RenderAdapter, RenderAdapterInfo, RenderDevice, RenderInstance, RenderQueue,
},
settings::WgpuSettings,
},
window::{PrimaryWindow, RawHandleWrapper},
};
use wgpu::Instance;
use crate::{
graphics,
input::XrInput,
resources::{
OXrSessionSetupInfo, XrEnvironmentBlendMode, XrFormat, XrFrameState, XrInstance,
XrResolution, XrSession, XrSessionRunning, XrSwapchain, XrViews,
},
};
#[derive(Resource, Clone)]
pub struct RenderCreationData {
pub device: RenderDevice,
pub queue: RenderQueue,
pub adapter_info: RenderAdapterInfo,
pub render_adapter: RenderAdapter,
pub instance: Arc<Instance>,
}
#[derive(Resource, Clone, ExtractResource)]
pub struct XrRenderData {
pub xr_instance: XrInstance,
pub xr_session: XrSession,
pub xr_blend_mode: XrEnvironmentBlendMode,
pub xr_resolution: XrResolution,
pub xr_format: XrFormat,
pub xr_session_running: XrSessionRunning,
// pub xr_frame_waiter: XrFrameWaiter,
pub xr_swapchain: XrSwapchain,
pub xr_input: XrInput,
pub xr_views: XrViews,
pub xr_frame_state: XrFrameState,
}
#[derive(Event, Clone, Copy, Debug)]
pub enum XrEnableRequest {
TryEnable,
TryDisable,
}
#[derive(Resource, Event, Copy, Clone, PartialEq, Eq, Reflect, ExtractResource)]
pub enum XrEnableStatus {
Enabled,
Disabled,
}
#[derive(Resource, Event, Copy, Clone, PartialEq, Eq, Debug, ExtractResource)]
pub enum XrNextEnabledState {
Enabled,
Disabled,
}
pub struct RenderRestartPlugin;
#[derive(Debug, ScheduleLabel, Clone, Copy, Hash, PartialEq, Eq)]
pub struct XrPreSetup;
#[derive(Debug, ScheduleLabel, Clone, Copy, Hash, PartialEq, Eq)]
pub struct XrSetup;
#[derive(Debug, ScheduleLabel, Clone, Copy, Hash, PartialEq, Eq)]
pub struct XrPrePostSetup;
#[derive(Debug, ScheduleLabel, Clone, Copy, Hash, PartialEq, Eq)]
pub struct XrPostSetup;
#[derive(Debug, ScheduleLabel, Clone, Copy, Hash, PartialEq, Eq)]
pub struct XrPreCleanup;
#[derive(Debug, ScheduleLabel, Clone, Copy, Hash, PartialEq, Eq)]
pub struct XrCleanup;
#[derive(Debug, ScheduleLabel, Clone, Copy, Hash, PartialEq, Eq)]
pub struct XrPostCleanup;
#[derive(Debug, ScheduleLabel, Clone, Copy, Hash, PartialEq, Eq)]
pub struct XrPreRenderUpdate;
#[derive(Debug, ScheduleLabel, Clone, Copy, Hash, PartialEq, Eq)]
pub struct XrRenderUpdate;
#[derive(Debug, ScheduleLabel, Clone, Copy, Hash, PartialEq, Eq)]
pub struct XrPostRenderUpdate;
pub fn xr_only() -> impl FnMut(Option<Res<'_, XrEnableStatus>>, Res<XrSessionRunning>) -> bool {
|status, running| {
resource_exists_and_equals(XrEnableStatus::Enabled)(status)
&& running.load(std::sync::atomic::Ordering::Relaxed)
}
}
impl Plugin for RenderRestartPlugin {
fn build(&self, app: &mut App) {
add_schedules(app);
app.add_plugins(ExtractResourcePlugin::<XrRenderData>::default())
.add_event::<XrEnableRequest>()
.add_event::<XrEnableStatus>()
.add_event::<StartXrSession>()
.add_event::<EndXrSession>()
.add_systems(
PreUpdate,
setup_xr
.run_if(|running: Res<XrSessionRunning>| {
running.load(std::sync::atomic::Ordering::Relaxed)
})
.run_if(run_once()),
)
.add_systems(
PostUpdate,
update_xr_stuff.run_if(on_event::<XrEnableRequest>()),
)
.add_systems(XrPreRenderUpdate, decide_next_xr_state)
.add_systems(XrPostRenderUpdate, clear_events)
.add_systems(
XrRenderUpdate,
(
cleanup_xr.run_if(resource_exists_and_equals(XrNextEnabledState::Disabled)),
// handle_xr_enable_requests,
apply_deferred,
setup_xr, /* .run_if(resource_exists_and_equals(XrEnableStatus::Enabled)) */
)
.chain(),
)
.add_systems(XrCleanup, cleanup_oxr_session);
app.add_systems(
PostUpdate,
start_xr_session.run_if(on_event::<StartXrSession>()),
);
app.add_systems(
PostUpdate,
stop_xr_session.run_if(on_event::<EndXrSession>()),
);
}
}
fn clear_events(mut events: ResMut<Events<XrEnableRequest>>) {
events.clear();
}
fn add_schedules(app: &mut App) {
let schedules = [
Schedule::new(XrPreSetup),
Schedule::new(XrSetup),
Schedule::new(XrPrePostSetup),
Schedule::new(XrPostSetup),
Schedule::new(XrPreRenderUpdate),
Schedule::new(XrRenderUpdate),
Schedule::new(XrPostRenderUpdate),
Schedule::new(XrPreCleanup),
Schedule::new(XrCleanup),
Schedule::new(XrPostCleanup),
];
for mut schedule in schedules {
schedule.set_executor_kind(ExecutorKind::SingleThreaded);
schedule.set_apply_final_deferred(true);
app.add_schedule(schedule);
}
}
pub fn setup_xr(world: &mut World) {
info!("running setup schedule :3");
world.insert_resource(XrEnableStatus::Enabled);
world.run_schedule(XrPreSetup);
world.run_schedule(XrSetup);
world.run_schedule(XrPrePostSetup);
world.run_schedule(XrPostSetup);
}
fn cleanup_xr(world: &mut World) {
world.run_schedule(XrPreCleanup);
world.run_schedule(XrCleanup);
world.run_schedule(XrPostCleanup);
}
fn cleanup_oxr_session(xr_status: Option<Res<XrEnableStatus>>, session: Option<ResMut<XrSession>>) {
if let (Some(XrEnableStatus::Disabled), Some(s)) = (xr_status.map(|v| v.into_inner()), session)
{
s.into_inner().request_exit().unwrap();
}
}
pub fn update_xr_stuff(world: &mut World) {
world.run_schedule(XrPreRenderUpdate);
world.run_schedule(XrRenderUpdate);
world.run_schedule(XrPostRenderUpdate);
}
#[derive(Event, Clone, Copy, Default)]
pub struct StartXrSession;
#[derive(Event, Clone, Copy, Default)]
pub struct EndXrSession;
fn start_xr_session(
mut commands: Commands,
instance: Res<XrInstance>,
primary_window: Query<&RawHandleWrapper, With<PrimaryWindow>>,
setup_info: NonSend<OXrSessionSetupInfo>,
render_device: Res<RenderDevice>,
render_adapter: Res<RenderAdapter>,
render_instance: Res<RenderInstance>,
) {
let (
xr_session,
xr_resolution,
xr_format,
xr_session_running,
xr_frame_waiter,
xr_swapchain,
xr_input,
xr_views,
xr_frame_state,
) = match graphics::start_xr_session(
primary_window.get_single().cloned().ok(),
&setup_info,
&instance,
&render_device,
&render_adapter,
&render_instance,
) {
Ok(data) => data,
Err(err) => {
error!("Unable to start OpenXR Session: {}", err);
return;
}
};
commands.insert_resource(xr_session);
commands.insert_resource(xr_resolution);
commands.insert_resource(xr_format);
commands.insert_resource(xr_session_running);
commands.insert_resource(xr_frame_waiter);
commands.insert_resource(xr_swapchain);
commands.insert_resource(xr_input);
commands.insert_resource(xr_views);
commands.insert_resource(xr_frame_state);
}
fn stop_xr_session(mut commands: Commands, session: ResMut<XrSession>) {
match session.request_exit() {
Ok(_) => {}
Err(err) => {
error!("Error while trying to request session exit: {}", err)
}
}
}
// fn handle_xr_enable_requests(
// primary_window: Query<&RawHandleWrapper, With<PrimaryWindow>>,
// mut commands: Commands,
// next_state: Res<XrNextEnabledState>,
// on_main: Option<NonSend<ForceMain>>,
// ) {
// // Just to force this system onto the main thread because of unsafe code
// let _ = on_main;
//
// let (creation_data, xr_data) = match next_state.into_inner() {
// XrNextEnabledState::Enabled => {
// let (
// device,
// queue,
// adapter_info,
// render_adapter,
// instance,
// xr_instance,
// session,
// blend_mode,
// resolution,
// format,
// session_running,
// frame_waiter,
// swapchain,
// input,
// views,
// frame_state,
// ) = graphics::initialize_xr_graphics(primary_window.get_single().ok().cloned())
// .unwrap();
//
// commands.insert_resource(XrEnableStatus::Enabled);
// (
// RenderCreationData {
// device,
// queue,
// adapter_info,
// render_adapter,
// instance: Arc::new(instance),
// },
// Some(XrRenderData {
// xr_instance,
// xr_session: session,
// xr_blend_mode: blend_mode,
// xr_resolution: resolution,
// xr_format: format,
// xr_session_running: session_running,
// xr_frame_waiter: frame_waiter,
// xr_swapchain: swapchain,
// xr_input: input,
// xr_views: views,
// xr_frame_state: frame_state,
// }),
// )
// }
// XrNextEnabledState::Disabled => (
// init_non_xr_graphics(primary_window.get_single().ok().cloned()),
// None,
// ),
// };
//
// commands.insert_resource(creation_data.device);
// commands.insert_resource(creation_data.queue);
// commands.insert_resource(RenderInstance(creation_data.instance));
// commands.insert_resource(creation_data.adapter_info);
// commands.insert_resource(creation_data.render_adapter);
//
// if let Some(xr_data) = xr_data {
// // TODO: Remove this lib.rs:144
// commands.insert_resource(xr_data.clone());
//
// commands.insert_resource(xr_data.xr_instance);
// commands.insert_resource(xr_data.xr_session);
// commands.insert_resource(xr_data.xr_blend_mode);
// commands.insert_resource(xr_data.xr_resolution);
// commands.insert_resource(xr_data.xr_format);
// commands.insert_resource(xr_data.xr_session_running);
// commands.insert_resource(xr_data.xr_frame_waiter);
// commands.insert_resource(xr_data.xr_input);
// commands.insert_resource(xr_data.xr_views);
// commands.insert_resource(xr_data.xr_frame_state);
// commands.insert_resource(xr_data.xr_swapchain);
// } else {
// commands.remove_resource::<XrRenderData>();
//
// commands.remove_resource::<XrInstance>();
// commands.remove_resource::<XrSession>();
// commands.remove_resource::<XrEnvironmentBlendMode>();
// commands.remove_resource::<XrResolution>();
// commands.remove_resource::<XrFormat>();
// commands.remove_resource::<XrSessionRunning>();
// commands.remove_resource::<XrFrameWaiter>();
// commands.remove_resource::<XrSwapchain>();
// commands.remove_resource::<XrInput>();
// commands.remove_resource::<XrViews>();
// commands.remove_resource::<XrFrameState>();
// }
// }
fn decide_next_xr_state(
mut commands: Commands,
mut events: EventReader<XrEnableRequest>,
xr_status: Option<Res<XrEnableStatus>>,
) {
info!("hm");
let request = match events.read().next() {
Some(v) => v,
None => return,
};
info!("ok");
match (request, xr_status.as_deref()) {
(XrEnableRequest::TryEnable, Some(XrEnableStatus::Enabled)) => {
info!("Xr Already Enabled! ignoring request");
return;
}
(XrEnableRequest::TryDisable, Some(XrEnableStatus::Disabled)) => {
info!("Xr Already Disabled! ignoring request");
return;
}
// (_, Some(XrEnableStatus::Waiting)) => {
// info!("Already Handling Request! ignoring request");
// return;
// }
_ => {}
}
let r = match request {
XrEnableRequest::TryEnable => XrNextEnabledState::Enabled,
XrEnableRequest::TryDisable => XrNextEnabledState::Disabled,
};
info!("{:#?}", r);
commands.insert_resource(r);
}
pub fn init_non_xr_graphics(primary_window: Option<RawHandleWrapper>) -> RenderCreationData {
let settings = WgpuSettings::default();
let async_renderer = async move {
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
// Probably a bad idea unwraping here but on the other hand no backends
backends: settings.backends.unwrap(),
dx12_shader_compiler: settings.dx12_shader_compiler.clone(),
});
let surface = primary_window.map(|wrapper| unsafe {
// SAFETY: Plugins should be set up on the main thread.
let handle = wrapper.get_handle();
instance
.create_surface(&handle)
.expect("Failed to create wgpu surface")
});
let request_adapter_options = wgpu::RequestAdapterOptions {
power_preference: settings.power_preference,
compatible_surface: surface.as_ref(),
..Default::default()
};
let (device, queue, adapter_info, render_adapter) =
renderer::initialize_renderer(&instance, &settings, &request_adapter_options).await;
debug!("Configured wgpu adapter Limits: {:#?}", device.limits());
debug!("Configured wgpu adapter Features: {:#?}", device.features());
RenderCreationData {
device,
queue,
adapter_info,
render_adapter,
instance: Arc::new(instance),
}
};
// No need for wasm in bevy_oxr web xr would be a different crate
futures_lite::future::block_on(async_renderer)
}

View File

@@ -1,6 +1,15 @@
use bevy::{app::PluginGroupBuilder, prelude::*}; use bevy::{app::PluginGroupBuilder, prelude::*};
use openxr::FormFactor;
use self::{emulated::HandEmulationPlugin, hand_tracking::HandTrackingPlugin}; use crate::{
resources::{XrInstance, XrSession},
xr_init::XrPreSetup,
};
use self::{
emulated::HandEmulationPlugin,
hand_tracking::{DisableHandTracking, HandTrackingData, HandTrackingPlugin},
};
pub mod common; pub mod common;
pub mod emulated; pub mod emulated;
@@ -8,12 +17,35 @@ pub mod hand_tracking;
pub struct XrHandPlugins; pub struct XrHandPlugins;
impl PluginGroup for XrHandPlugins { impl Plugin for XrHandPlugins {
fn build(self) -> PluginGroupBuilder { fn build(&self, app: &mut App) {
PluginGroupBuilder::start::<Self>() app.add_plugins(HandTrackingPlugin)
.add(HandTrackingPlugin) .add_plugins(HandPlugin)
.add(HandEmulationPlugin) .add_plugins(HandEmulationPlugin);
.build() }
}
pub struct HandPlugin;
impl Plugin for HandPlugin {
fn build(&self, app: &mut App) {
app.add_systems(XrPreSetup, check_for_handtracking);
}
}
fn check_for_handtracking(
mut commands: Commands,
instance: Res<XrInstance>,
session: Res<XrSession>,
) {
let hands = instance.exts().ext_hand_tracking.is_some()
&& instance
.supports_hand_tracking(instance.system(FormFactor::HEAD_MOUNTED_DISPLAY).unwrap())
.is_ok_and(|v| v);
if hands {
commands.insert_resource(HandTrackingData::new(&session).unwrap());
} else {
commands.insert_resource(DisableHandTracking::Both);
} }
} }

View File

@@ -10,11 +10,11 @@ pub mod trackers;
pub mod xr_camera; pub mod xr_camera;
use crate::resources::{XrInstance, XrSession}; use crate::resources::{XrInstance, XrSession};
use crate::xr_begin_frame; use crate::xr_init::{xr_only, XrPostSetup, XrPreSetup, XrSetup};
use crate::xr_init::{xr_only, XrPostSetup, XrSetup, XrPreSetup};
use crate::xr_input::controllers::XrControllerType; use crate::xr_input::controllers::XrControllerType;
use crate::xr_input::oculus_touch::setup_oculus_controller; use crate::xr_input::oculus_touch::setup_oculus_controller;
use crate::xr_input::xr_camera::{xr_camera_head_sync, Eye, XRProjection, XrCameraBundle}; use crate::xr_input::xr_camera::{xr_camera_head_sync, Eye, XRProjection, XrCameraBundle};
use crate::{locate_views, xr_wait_frame};
use bevy::app::{App, PostUpdate, Startup}; use bevy::app::{App, PostUpdate, Startup};
use bevy::ecs::entity::Entity; use bevy::ecs::entity::Entity;
use bevy::ecs::query::With; use bevy::ecs::query::With;
@@ -24,17 +24,19 @@ use bevy::math::Vec2;
use bevy::prelude::{BuildChildren, Component, Deref, DerefMut, IntoSystemConfigs, Resource}; use bevy::prelude::{BuildChildren, Component, Deref, DerefMut, IntoSystemConfigs, Resource};
use bevy::prelude::{Commands, Plugin, PreUpdate, Quat, Res, SpatialBundle, Update, Vec3}; use bevy::prelude::{Commands, Plugin, PreUpdate, Quat, Res, SpatialBundle, Update, Vec3};
use bevy::render::camera::CameraProjectionPlugin; use bevy::render::camera::CameraProjectionPlugin;
use bevy::render::extract_component::ExtractComponentPlugin;
use bevy::render::view::{update_frusta, VisibilitySystems}; use bevy::render::view::{update_frusta, VisibilitySystems};
use bevy::transform::TransformSystem; use bevy::transform::TransformSystem;
use bevy::utils::HashMap; use bevy::utils::HashMap;
use openxr::Binding; use openxr::Binding;
use self::actions::{setup_oxr_actions, OpenXrActionsPlugin}; use self::actions::{setup_oxr_actions, OpenXrActionsPlugin};
use self::oculus_touch::{post_action_setup_oculus_controller, ActionSets, init_subaction_path}; use self::oculus_touch::{init_subaction_path, post_action_setup_oculus_controller, ActionSets};
use self::trackers::{ use self::trackers::{
adopt_open_xr_trackers, update_open_xr_controllers, OpenXRLeftEye, OpenXRRightEye, adopt_open_xr_trackers, update_open_xr_controllers, OpenXRLeftEye, OpenXRRightEye,
OpenXRTrackingRoot, OpenXRTrackingRoot,
}; };
use self::xr_camera::{XrCameraType, TransformExtract};
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct OpenXrInput { pub struct OpenXrInput {
@@ -67,7 +69,10 @@ impl Plugin for OpenXrInput {
app.add_systems(PreUpdate, action_set_system.run_if(xr_only())); app.add_systems(PreUpdate, action_set_system.run_if(xr_only()));
app.add_systems( app.add_systems(
PreUpdate, PreUpdate,
xr_camera_head_sync.run_if(xr_only()).after(xr_begin_frame), xr_camera_head_sync
.run_if(xr_only())
.after(xr_wait_frame)
.after(locate_views),
); );
//update controller trackers //update controller trackers
app.add_systems(Update, update_open_xr_controllers.run_if(xr_only())); app.add_systems(Update, update_open_xr_controllers.run_if(xr_only()));
@@ -79,6 +84,9 @@ impl Plugin for OpenXrInput {
); );
app.add_systems(XrPreSetup, init_subaction_path); app.add_systems(XrPreSetup, init_subaction_path);
app.add_systems(XrSetup, setup_xr_cameras); app.add_systems(XrSetup, setup_xr_cameras);
app.add_plugins(ExtractComponentPlugin::<XrCameraType>::default());
app.add_plugins(ExtractComponentPlugin::<XRProjection>::default());
app.add_plugins(ExtractComponentPlugin::<TransformExtract>::default());
} }
} }

View File

@@ -1,9 +1,11 @@
use crate::xr_input::{QuatConv, Vec3Conv}; use crate::xr_input::{QuatConv, Vec3Conv};
use crate::{LEFT_XR_TEXTURE_HANDLE, RIGHT_XR_TEXTURE_HANDLE}; use crate::{LEFT_XR_TEXTURE_HANDLE, RIGHT_XR_TEXTURE_HANDLE};
use bevy::core_pipeline::tonemapping::{DebandDither, Tonemapping}; use bevy::core_pipeline::tonemapping::{DebandDither, Tonemapping};
use bevy::ecs::system::lifetimeless::Read;
use bevy::math::Vec3A; use bevy::math::Vec3A;
use bevy::prelude::*; use bevy::prelude::*;
use bevy::render::camera::{CameraProjection, CameraRenderGraph, RenderTarget}; use bevy::render::camera::{CameraProjection, CameraRenderGraph, RenderTarget};
use bevy::render::extract_component::ExtractComponent;
use bevy::render::primitives::Frustum; use bevy::render::primitives::Frustum;
use bevy::render::view::{ColorGrading, VisibleEntities}; use bevy::render::view::{ColorGrading, VisibleEntities};
use openxr::Fovf; use openxr::Fovf;
@@ -48,6 +50,35 @@ pub enum XrCameraType {
Flatscreen, Flatscreen,
} }
#[derive(Component)]
pub(super) struct TransformExtract;
impl ExtractComponent for TransformExtract {
type Query = Read<Transform>;
type Filter = ();
type Out = Transform;
fn extract_component(item: bevy::ecs::query::QueryItem<'_, Self::Query>) -> Option<Self::Out> {
Some(*item)
}
}
impl ExtractComponent for XrCameraType {
type Query = Read<Self>;
type Filter = ();
type Out = Self;
fn extract_component(item: bevy::ecs::query::QueryItem<'_, Self::Query>) -> Option<Self::Out> {
Some(*item)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub enum Eye { pub enum Eye {
Left = 0, Left = 0,
@@ -81,7 +112,7 @@ impl XrCameraBundle {
} }
} }
#[derive(Debug, Clone, Component, Reflect)] #[derive(Debug, Clone, Component, Reflect, ExtractComponent)]
#[reflect(Component, Default)] #[reflect(Component, Default)]
pub struct XRProjection { pub struct XRProjection {
pub near: f32, pub near: f32,
@@ -240,25 +271,22 @@ impl CameraProjection for XRProjection {
} }
pub fn xr_camera_head_sync( pub fn xr_camera_head_sync(
views: ResMut<crate::resources::XrViews>,
query: Query<(&mut Transform, &XrCameraType, &mut XRProjection)>,
) {
fn f(
views: ResMut<crate::resources::XrViews>, views: ResMut<crate::resources::XrViews>,
mut query: Query<(&mut Transform, &XrCameraType, &mut XRProjection)>, mut query: Query<(&mut Transform, &XrCameraType, &mut XRProjection)>,
) -> Option<()> { ) {
//TODO calculate HMD position //TODO calculate HMD position
for (mut transform, camera_type, mut xr_projection) in query.iter_mut() { for (mut transform, camera_type, mut xr_projection) in query.iter_mut() {
let view_idx = match camera_type { let view_idx = match camera_type {
XrCameraType::Xr(eye) => *eye as usize, XrCameraType::Xr(eye) => *eye as usize,
XrCameraType::Flatscreen => return None, // I don't belive we need a flatscrenn cam, that's just a cam without this component
XrCameraType::Flatscreen => continue,
};
let view = match views.get(view_idx) {
Some(views) => views,
None => continue,
}; };
let view = views.get(view_idx)?;
xr_projection.fov = view.fov; xr_projection.fov = view.fov;
transform.rotation = view.pose.orientation.to_quat(); transform.rotation = view.pose.orientation.to_quat();
transform.translation = view.pose.position.to_vec3(); transform.translation = view.pose.position.to_vec3();
} }
Some(())
}
let _ = f(views, query);
} }