fixed judder
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
use crate::{init::OxrPreUpdateSet, resources::OxrSession};
|
use crate::resources::OxrSession;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_xr::session::session_running;
|
use bevy_xr::session::session_running;
|
||||||
|
|
||||||
@@ -7,9 +7,7 @@ impl Plugin for OxrActionSyncingPlugin {
|
|||||||
app.add_event::<OxrSyncActionSet>();
|
app.add_event::<OxrSyncActionSet>();
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
PreUpdate,
|
PreUpdate,
|
||||||
sync_sets
|
sync_sets.run_if(session_running), // .in_set(OxrPreUpdateSet::SyncActions),
|
||||||
.run_if(session_running)
|
|
||||||
.in_set(OxrPreUpdateSet::SyncActions),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use openxr::SpaceLocationFlags;
|
|||||||
use crate::{
|
use crate::{
|
||||||
init::OxrTrackingRoot,
|
init::OxrTrackingRoot,
|
||||||
reference_space::{OxrPrimaryReferenceSpace, OxrReferenceSpace},
|
reference_space::{OxrPrimaryReferenceSpace, OxrReferenceSpace},
|
||||||
resources::{OxrSession, OxrTime},
|
resources::{OxrFrameState, OxrSession},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct HandTrackingPlugin {
|
pub struct HandTrackingPlugin {
|
||||||
@@ -132,7 +132,7 @@ pub struct OxrHandTracker(pub openxr::HandTracker);
|
|||||||
|
|
||||||
fn locate_hands(
|
fn locate_hands(
|
||||||
default_ref_space: Res<OxrPrimaryReferenceSpace>,
|
default_ref_space: Res<OxrPrimaryReferenceSpace>,
|
||||||
time: Res<OxrTime>,
|
frame_state: Res<OxrFrameState>,
|
||||||
tracker_query: Query<(
|
tracker_query: Query<(
|
||||||
&OxrHandTracker,
|
&OxrHandTracker,
|
||||||
Option<&OxrReferenceSpace>,
|
Option<&OxrReferenceSpace>,
|
||||||
@@ -143,7 +143,8 @@ fn locate_hands(
|
|||||||
for (tracker, ref_space, hand_entities) in &tracker_query {
|
for (tracker, ref_space, hand_entities) in &tracker_query {
|
||||||
let ref_space = ref_space.map(|v| &v.0).unwrap_or(&default_ref_space.0);
|
let ref_space = ref_space.map(|v| &v.0).unwrap_or(&default_ref_space.0);
|
||||||
// relate_hand_joints also provides velocities
|
// relate_hand_joints also provides velocities
|
||||||
let joints = match ref_space.locate_hand_joints(tracker, **time) {
|
let joints = match ref_space.locate_hand_joints(tracker, frame_state.predicted_display_time)
|
||||||
|
{
|
||||||
Ok(Some(v)) => v,
|
Ok(Some(v)) => v,
|
||||||
Ok(None) => continue,
|
Ok(None) => continue,
|
||||||
Err(openxr::sys::Result::ERROR_EXTENSION_NOT_PRESENT) => {
|
Err(openxr::sys::Result::ERROR_EXTENSION_NOT_PRESENT) => {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use bevy::app::MainScheduleOrder;
|
||||||
|
use bevy::ecs::schedule::ScheduleLabel;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::render::extract_resource::ExtractResourcePlugin;
|
use bevy::render::extract_resource::ExtractResourcePlugin;
|
||||||
use bevy::render::renderer::RenderAdapter;
|
use bevy::render::renderer::RenderAdapter;
|
||||||
@@ -14,21 +16,17 @@ use bevy::render::RenderSet;
|
|||||||
use bevy::transform::TransformSystem;
|
use bevy::transform::TransformSystem;
|
||||||
use bevy::winit::UpdateMode;
|
use bevy::winit::UpdateMode;
|
||||||
use bevy::winit::WinitSettings;
|
use bevy::winit::WinitSettings;
|
||||||
use bevy_xr::session::handle_session;
|
|
||||||
use bevy_xr::session::session_available;
|
|
||||||
use bevy_xr::session::session_running;
|
use bevy_xr::session::session_running;
|
||||||
use bevy_xr::session::status_equals;
|
use bevy_xr::session::status_equals;
|
||||||
use bevy_xr::session::BeginXrSession;
|
use bevy_xr::session::BeginXrSession;
|
||||||
use bevy_xr::session::CreateXrSession;
|
use bevy_xr::session::CreateXrSession;
|
||||||
use bevy_xr::session::DestroyXrSession;
|
use bevy_xr::session::DestroyXrSession;
|
||||||
use bevy_xr::session::EndXrSession;
|
use bevy_xr::session::EndXrSession;
|
||||||
use bevy_xr::session::XrSharedStatus;
|
|
||||||
use bevy_xr::session::XrStatus;
|
use bevy_xr::session::XrStatus;
|
||||||
use bevy_xr::session::XrStatusChanged;
|
use bevy_xr::session::XrStatusChanged;
|
||||||
|
|
||||||
use crate::error::OxrError;
|
use crate::error::OxrError;
|
||||||
use crate::graphics::*;
|
use crate::graphics::*;
|
||||||
use crate::reference_space::OxrPrimaryReferenceSpace;
|
|
||||||
use crate::resources::*;
|
use crate::resources::*;
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
|
|
||||||
@@ -36,15 +34,20 @@ pub fn session_started(started: Option<Res<OxrSessionStarted>>) -> bool {
|
|||||||
started.is_some_and(|started| started.get())
|
started.is_some_and(|started| started.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, SystemSet)]
|
pub fn should_render(frame_state: Option<Res<OxrFrameState>>) -> bool {
|
||||||
pub enum OxrPreUpdateSet {
|
frame_state.is_some_and(|frame_state| frame_state.should_render)
|
||||||
PollEvents,
|
|
||||||
HandleEvents,
|
|
||||||
UpdateCriticalComponents,
|
|
||||||
UpdateNonCriticalComponents,
|
|
||||||
SyncActions,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO!() better name pls
|
||||||
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct OxrLast;
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, SystemSet)]
|
||||||
|
pub struct OxrHandleEvents;
|
||||||
|
|
||||||
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct OxrSessionCreated;
|
||||||
|
|
||||||
pub struct OxrInitPlugin {
|
pub struct OxrInitPlugin {
|
||||||
/// Information about the app this is being used to build.
|
/// Information about the app this is being used to build.
|
||||||
pub app_info: AppInfo,
|
pub app_info: AppInfo,
|
||||||
@@ -93,8 +96,6 @@ impl Plugin for OxrInitPlugin {
|
|||||||
WgpuGraphics(device, queue, adapter_info, adapter, wgpu_instance),
|
WgpuGraphics(device, queue, adapter_info, adapter, wgpu_instance),
|
||||||
session_create_info,
|
session_create_info,
|
||||||
)) => {
|
)) => {
|
||||||
let status = XrSharedStatus::new(XrStatus::Available);
|
|
||||||
|
|
||||||
app.add_plugins((
|
app.add_plugins((
|
||||||
RenderPlugin {
|
RenderPlugin {
|
||||||
render_creation: RenderCreation::manual(
|
render_creation: RenderCreation::manual(
|
||||||
@@ -107,19 +108,16 @@ impl Plugin for OxrInitPlugin {
|
|||||||
synchronous_pipeline_compilation: self.synchronous_pipeline_compilation,
|
synchronous_pipeline_compilation: self.synchronous_pipeline_compilation,
|
||||||
},
|
},
|
||||||
ExtractResourcePlugin::<OxrCleanupSession>::default(),
|
ExtractResourcePlugin::<OxrCleanupSession>::default(),
|
||||||
ExtractResourcePlugin::<OxrTime>::default(),
|
ExtractResourcePlugin::<XrStatus>::default(),
|
||||||
ExtractResourcePlugin::<OxrRootTransform>::default(),
|
|
||||||
))
|
))
|
||||||
.add_systems(First, reset_per_frame_resources)
|
.init_schedule(OxrLast)
|
||||||
|
.init_schedule(OxrSessionCreated)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
PreUpdate,
|
OxrLast,
|
||||||
(
|
(
|
||||||
poll_events
|
reset_per_frame_resources,
|
||||||
.run_if(session_available)
|
poll_events,
|
||||||
.in_set(OxrPreUpdateSet::PollEvents),
|
create_xr_session
|
||||||
(
|
|
||||||
(create_xr_session, apply_deferred)
|
|
||||||
.chain()
|
|
||||||
.run_if(on_event::<CreateXrSession>())
|
.run_if(on_event::<CreateXrSession>())
|
||||||
.run_if(status_equals(XrStatus::Available)),
|
.run_if(status_equals(XrStatus::Available)),
|
||||||
begin_xr_session
|
begin_xr_session
|
||||||
@@ -132,8 +130,8 @@ impl Plugin for OxrInitPlugin {
|
|||||||
.run_if(on_event::<DestroyXrSession>())
|
.run_if(on_event::<DestroyXrSession>())
|
||||||
.run_if(status_equals(XrStatus::Exiting)),
|
.run_if(status_equals(XrStatus::Exiting)),
|
||||||
)
|
)
|
||||||
.in_set(OxrPreUpdateSet::HandleEvents),
|
.chain()
|
||||||
),
|
.in_set(OxrHandleEvents),
|
||||||
)
|
)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
PostUpdate,
|
PostUpdate,
|
||||||
@@ -141,25 +139,25 @@ impl Plugin for OxrInitPlugin {
|
|||||||
)
|
)
|
||||||
.insert_resource(instance.clone())
|
.insert_resource(instance.clone())
|
||||||
.insert_resource(system_id)
|
.insert_resource(system_id)
|
||||||
.insert_resource(status.clone())
|
.insert_resource(XrStatus::Available)
|
||||||
.insert_resource(WinitSettings {
|
.insert_resource(WinitSettings {
|
||||||
focused_mode: UpdateMode::Continuous,
|
focused_mode: UpdateMode::Continuous,
|
||||||
unfocused_mode: UpdateMode::Continuous,
|
unfocused_mode: UpdateMode::Continuous,
|
||||||
})
|
})
|
||||||
.init_resource::<OxrCleanupSession>()
|
.init_resource::<OxrCleanupSession>()
|
||||||
.init_resource::<OxrRootTransform>()
|
.init_resource::<OxrRootTransform>()
|
||||||
.insert_non_send_resource(session_create_info);
|
.insert_non_send_resource(session_create_info)
|
||||||
|
.configure_sets(OxrLast, OxrHandleEvents);
|
||||||
|
|
||||||
|
app.world
|
||||||
|
.resource_mut::<MainScheduleOrder>()
|
||||||
|
.insert_after(Last, OxrLast);
|
||||||
|
|
||||||
app.world
|
app.world
|
||||||
.spawn((TransformBundle::default(), OxrTrackingRoot));
|
.spawn((TransformBundle::default(), OxrTrackingRoot));
|
||||||
|
|
||||||
let render_app = app.sub_app_mut(RenderApp);
|
let render_app = app.sub_app_mut(RenderApp);
|
||||||
render_app
|
render_app
|
||||||
.insert_resource(instance)
|
|
||||||
.insert_resource(system_id)
|
|
||||||
.insert_resource(status)
|
|
||||||
.init_resource::<OxrRootTransform>()
|
|
||||||
.init_resource::<OxrCleanupSession>()
|
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Render,
|
Render,
|
||||||
destroy_xr_session_render
|
destroy_xr_session_render
|
||||||
@@ -169,32 +167,19 @@ impl Plugin for OxrInitPlugin {
|
|||||||
.add_systems(
|
.add_systems(
|
||||||
ExtractSchedule,
|
ExtractSchedule,
|
||||||
transfer_xr_resources.run_if(not(session_running)),
|
transfer_xr_resources.run_if(not(session_running)),
|
||||||
);
|
)
|
||||||
|
.insert_resource(instance)
|
||||||
|
.insert_resource(system_id)
|
||||||
|
.init_resource::<OxrRootTransform>()
|
||||||
|
.init_resource::<OxrCleanupSession>();
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Failed to initialize openxr: {e}");
|
error!("Failed to initialize openxr: {e}");
|
||||||
let status = XrSharedStatus::new(XrStatus::Unavailable);
|
|
||||||
|
|
||||||
app.add_plugins(RenderPlugin::default())
|
app.add_plugins(RenderPlugin::default())
|
||||||
.insert_resource(status.clone());
|
.insert_resource(XrStatus::Unavailable);
|
||||||
|
|
||||||
let render_app = app.sub_app_mut(RenderApp);
|
|
||||||
|
|
||||||
render_app.insert_resource(status);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
app.configure_sets(
|
|
||||||
PreUpdate,
|
|
||||||
(
|
|
||||||
OxrPreUpdateSet::PollEvents.before(handle_session),
|
|
||||||
OxrPreUpdateSet::HandleEvents.after(handle_session),
|
|
||||||
OxrPreUpdateSet::UpdateCriticalComponents,
|
|
||||||
OxrPreUpdateSet::UpdateNonCriticalComponents,
|
|
||||||
)
|
|
||||||
.chain(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let session_started = OxrSessionStarted::default();
|
let session_started = OxrSessionStarted::default();
|
||||||
|
|
||||||
app.insert_resource(session_started.clone());
|
app.insert_resource(session_started.clone());
|
||||||
@@ -214,17 +199,12 @@ pub fn update_root_transform(
|
|||||||
root_transform.0 = *transform;
|
root_transform.0 = *transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn xr_entry() -> Result<OxrEntry> {
|
|
||||||
#[cfg(windows)]
|
|
||||||
let entry = openxr::Entry::linked();
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let entry = unsafe { openxr::Entry::load()? };
|
|
||||||
Ok(OxrEntry(entry))
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OxrInitPlugin {
|
impl OxrInitPlugin {
|
||||||
fn init_xr(&self) -> Result<(OxrInstance, OxrSystemId, WgpuGraphics, SessionConfigInfo)> {
|
fn init_xr(&self) -> Result<(OxrInstance, OxrSystemId, WgpuGraphics, SessionConfigInfo)> {
|
||||||
let entry = xr_entry()?;
|
#[cfg(windows)]
|
||||||
|
let entry = OxrEntry(openxr::Entry::linked());
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let entry = OxrEntry(unsafe { openxr::Entry::load()? });
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
entry.initialize_android_loader()?;
|
entry.initialize_android_loader()?;
|
||||||
@@ -301,6 +281,51 @@ impl OxrInitPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reset_per_frame_resources(mut cleanup: ResMut<OxrCleanupSession>) {
|
||||||
|
**cleanup = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Polls any OpenXR events and handles them accordingly
|
||||||
|
pub fn poll_events(
|
||||||
|
instance: Res<OxrInstance>,
|
||||||
|
mut status: ResMut<XrStatus>,
|
||||||
|
mut changed_event: EventWriter<XrStatusChanged>,
|
||||||
|
) {
|
||||||
|
let _span = info_span!("xr_poll_events");
|
||||||
|
let mut buffer = Default::default();
|
||||||
|
while let Some(event) = instance
|
||||||
|
.poll_event(&mut buffer)
|
||||||
|
.expect("Failed to poll event")
|
||||||
|
{
|
||||||
|
use openxr::Event::*;
|
||||||
|
match event {
|
||||||
|
SessionStateChanged(state) => {
|
||||||
|
use openxr::SessionState;
|
||||||
|
|
||||||
|
let state = state.state();
|
||||||
|
|
||||||
|
info!("entered XR state {:?}", state);
|
||||||
|
|
||||||
|
let new_status = match state {
|
||||||
|
SessionState::IDLE => XrStatus::Idle,
|
||||||
|
SessionState::READY => XrStatus::Ready,
|
||||||
|
SessionState::SYNCHRONIZED | SessionState::VISIBLE | SessionState::FOCUSED => {
|
||||||
|
XrStatus::Running
|
||||||
|
}
|
||||||
|
SessionState::STOPPING => XrStatus::Stopping,
|
||||||
|
SessionState::EXITING | SessionState::LOSS_PENDING => XrStatus::Exiting,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
changed_event.send(XrStatusChanged(new_status));
|
||||||
|
*status = new_status;
|
||||||
|
}
|
||||||
|
InstanceLossPending(_) => {}
|
||||||
|
EventsLost(e) => warn!("lost {} XR events", e.lost_event_count()),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn init_xr_session(
|
fn init_xr_session(
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
instance: &OxrInstance,
|
instance: &OxrInstance,
|
||||||
@@ -435,23 +460,11 @@ fn init_xr_session(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is used solely to transport resources from the main world to the render world.
|
pub fn create_xr_session(world: &mut World) {
|
||||||
#[derive(Resource)]
|
let device = world.resource::<RenderDevice>();
|
||||||
struct OxrRenderResources {
|
let instance = world.resource::<OxrInstance>();
|
||||||
session: OxrSession,
|
let create_info = world.non_send_resource::<SessionConfigInfo>();
|
||||||
frame_stream: OxrFrameStream,
|
let system_id = world.resource::<OxrSystemId>();
|
||||||
swapchain: OxrSwapchain,
|
|
||||||
images: OxrSwapchainImages,
|
|
||||||
graphics_info: OxrGraphicsInfo,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_xr_session(
|
|
||||||
device: Res<RenderDevice>,
|
|
||||||
instance: Res<OxrInstance>,
|
|
||||||
create_info: NonSend<SessionConfigInfo>,
|
|
||||||
system_id: Res<OxrSystemId>,
|
|
||||||
mut commands: Commands,
|
|
||||||
) {
|
|
||||||
match init_xr_session(
|
match init_xr_session(
|
||||||
device.wgpu_device(),
|
device.wgpu_device(),
|
||||||
&instance,
|
&instance,
|
||||||
@@ -459,17 +472,18 @@ pub fn create_xr_session(
|
|||||||
create_info.clone(),
|
create_info.clone(),
|
||||||
) {
|
) {
|
||||||
Ok((session, frame_waiter, frame_stream, swapchain, images, graphics_info)) => {
|
Ok((session, frame_waiter, frame_stream, swapchain, images, graphics_info)) => {
|
||||||
commands.insert_resource(session.clone());
|
world.insert_resource(session.clone());
|
||||||
commands.insert_resource(frame_waiter);
|
world.insert_resource(frame_waiter);
|
||||||
commands.insert_resource(images.clone());
|
world.insert_resource(images.clone());
|
||||||
commands.insert_resource(graphics_info.clone());
|
world.insert_resource(graphics_info.clone());
|
||||||
commands.insert_resource(OxrRenderResources {
|
world.insert_resource(OxrRenderResources {
|
||||||
session,
|
session,
|
||||||
frame_stream,
|
frame_stream,
|
||||||
swapchain,
|
swapchain,
|
||||||
images,
|
images,
|
||||||
graphics_info,
|
graphics_info,
|
||||||
});
|
});
|
||||||
|
world.run_schedule(OxrSessionCreated);
|
||||||
}
|
}
|
||||||
Err(e) => error!("Failed to initialize XrSession: {e}"),
|
Err(e) => error!("Failed to initialize XrSession: {e}"),
|
||||||
}
|
}
|
||||||
@@ -489,6 +503,16 @@ pub fn end_xr_session(session: Res<OxrSession>, session_started: Res<OxrSessionS
|
|||||||
session_started.set(false);
|
session_started.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This is used solely to transport resources from the main world to the render world.
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct OxrRenderResources {
|
||||||
|
session: OxrSession,
|
||||||
|
frame_stream: OxrFrameStream,
|
||||||
|
swapchain: OxrSwapchain,
|
||||||
|
images: OxrSwapchainImages,
|
||||||
|
graphics_info: OxrGraphicsInfo,
|
||||||
|
}
|
||||||
|
|
||||||
/// This system transfers important render resources from the main world to the render world when a session is created.
|
/// This system transfers important render resources from the main world to the render world when a session is created.
|
||||||
pub fn transfer_xr_resources(mut commands: Commands, mut world: ResMut<MainWorld>) {
|
pub fn transfer_xr_resources(mut commands: Commands, mut world: ResMut<MainWorld>) {
|
||||||
let Some(OxrRenderResources {
|
let Some(OxrRenderResources {
|
||||||
@@ -509,64 +533,17 @@ pub fn transfer_xr_resources(mut commands: Commands, mut world: ResMut<MainWorld
|
|||||||
commands.insert_resource(graphics_info);
|
commands.insert_resource(graphics_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Polls any OpenXR events and handles them accordingly
|
pub fn destroy_xr_session(world: &mut World) {
|
||||||
pub fn poll_events(
|
world.remove_resource::<OxrSession>();
|
||||||
instance: Res<OxrInstance>,
|
world.remove_resource::<OxrFrameWaiter>();
|
||||||
status: Res<XrSharedStatus>,
|
world.remove_resource::<OxrSwapchainImages>();
|
||||||
mut changed_event: EventWriter<XrStatusChanged>,
|
world.remove_resource::<OxrGraphicsInfo>();
|
||||||
) {
|
world.insert_resource(OxrCleanupSession(true));
|
||||||
let _span = info_span!("xr_poll_events");
|
|
||||||
let mut buffer = Default::default();
|
|
||||||
while let Some(event) = instance
|
|
||||||
.poll_event(&mut buffer)
|
|
||||||
.expect("Failed to poll event")
|
|
||||||
{
|
|
||||||
use openxr::Event::*;
|
|
||||||
match event {
|
|
||||||
SessionStateChanged(state) => {
|
|
||||||
use openxr::SessionState;
|
|
||||||
|
|
||||||
let state = state.state();
|
|
||||||
|
|
||||||
info!("entered XR state {:?}", state);
|
|
||||||
|
|
||||||
let new_status = match state {
|
|
||||||
SessionState::IDLE => XrStatus::Idle,
|
|
||||||
SessionState::READY => XrStatus::Ready,
|
|
||||||
SessionState::SYNCHRONIZED | SessionState::VISIBLE | SessionState::FOCUSED => {
|
|
||||||
XrStatus::Running
|
|
||||||
}
|
|
||||||
SessionState::STOPPING => XrStatus::Stopping,
|
|
||||||
SessionState::EXITING | SessionState::LOSS_PENDING => XrStatus::Exiting,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
changed_event.send(XrStatusChanged(new_status));
|
|
||||||
status.set(new_status);
|
|
||||||
}
|
|
||||||
InstanceLossPending(_) => {}
|
|
||||||
EventsLost(e) => warn!("lost {} XR events", e.lost_event_count()),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset_per_frame_resources(mut cleanup: ResMut<OxrCleanupSession>) {
|
|
||||||
**cleanup = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn destroy_xr_session(mut commands: Commands) {
|
|
||||||
commands.remove_resource::<OxrSession>();
|
|
||||||
commands.remove_resource::<OxrFrameWaiter>();
|
|
||||||
commands.remove_resource::<OxrSwapchainImages>();
|
|
||||||
commands.remove_resource::<OxrGraphicsInfo>();
|
|
||||||
commands.remove_resource::<OxrPrimaryReferenceSpace>();
|
|
||||||
commands.insert_resource(OxrCleanupSession(true));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy_xr_session_render(world: &mut World) {
|
pub fn destroy_xr_session_render(world: &mut World) {
|
||||||
world.remove_resource::<OxrSwapchain>();
|
world.remove_resource::<OxrSwapchain>();
|
||||||
world.remove_resource::<OxrFrameStream>();
|
world.remove_resource::<OxrFrameStream>();
|
||||||
world.remove_resource::<OxrPrimaryReferenceSpace>();
|
|
||||||
world.remove_resource::<OxrSwapchainImages>();
|
world.remove_resource::<OxrSwapchainImages>();
|
||||||
world.remove_resource::<OxrGraphicsInfo>();
|
world.remove_resource::<OxrGraphicsInfo>();
|
||||||
world.remove_resource::<OxrSession>();
|
world.remove_resource::<OxrSession>();
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use crate::reference_space::OxrPrimaryReferenceSpace;
|
|||||||
use crate::resources::*;
|
use crate::resources::*;
|
||||||
|
|
||||||
pub trait LayerProvider {
|
pub trait LayerProvider {
|
||||||
fn get<'a>(&'a self, world: &'a World) -> Box<dyn CompositionLayer + '_>;
|
fn get<'a>(&'a self, world: &'a World) -> Option<Box<dyn CompositionLayer + '_>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ProjectionLayer;
|
pub struct ProjectionLayer;
|
||||||
@@ -16,11 +16,11 @@ pub struct ProjectionLayer;
|
|||||||
pub struct PassthroughLayer;
|
pub struct PassthroughLayer;
|
||||||
|
|
||||||
impl LayerProvider for ProjectionLayer {
|
impl LayerProvider for ProjectionLayer {
|
||||||
fn get<'a>(&self, world: &'a World) -> Box<dyn CompositionLayer<'a> + 'a> {
|
fn get<'a>(&self, world: &'a World) -> Option<Box<dyn CompositionLayer<'a> + 'a>> {
|
||||||
let stage = world.resource::<OxrPrimaryReferenceSpace>();
|
let stage = world.get_resource::<OxrPrimaryReferenceSpace>()?;
|
||||||
let openxr_views = world.resource::<OxrViews>();
|
let openxr_views = world.get_resource::<OxrViews>()?;
|
||||||
let swapchain = world.resource::<OxrSwapchain>();
|
let swapchain = world.get_resource::<OxrSwapchain>()?;
|
||||||
let graphics_info = world.resource::<OxrGraphicsInfo>();
|
let graphics_info = world.get_resource::<OxrGraphicsInfo>()?;
|
||||||
let rect = openxr::Rect2Di {
|
let rect = openxr::Rect2Di {
|
||||||
offset: openxr::Offset2Di { x: 0, y: 0 },
|
offset: openxr::Offset2Di { x: 0, y: 0 },
|
||||||
extent: openxr::Extent2Di {
|
extent: openxr::Extent2Di {
|
||||||
@@ -29,7 +29,11 @@ impl LayerProvider for ProjectionLayer {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Box::new(
|
if openxr_views.len() < 2 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Box::new(
|
||||||
CompositionLayerProjection::new()
|
CompositionLayerProjection::new()
|
||||||
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
|
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
|
||||||
.space(&stage)
|
.space(&stage)
|
||||||
@@ -53,17 +57,17 @@ impl LayerProvider for ProjectionLayer {
|
|||||||
.image_rect(rect),
|
.image_rect(rect),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayerProvider for PassthroughLayer {
|
impl LayerProvider for PassthroughLayer {
|
||||||
fn get<'a>(&'a self, world: &'a World) -> Box<dyn CompositionLayer + '_> {
|
fn get<'a>(&'a self, world: &'a World) -> Option<Box<dyn CompositionLayer + '_>> {
|
||||||
Box::new(
|
Some(Box::new(
|
||||||
CompositionLayerPassthrough::new()
|
CompositionLayerPassthrough::new()
|
||||||
.layer_handle(world.resource::<OxrPassthroughLayer>())
|
.layer_handle(world.get_resource::<OxrPassthroughLayer>()?)
|
||||||
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA),
|
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA),
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,8 @@ use bevy::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
render::extract_resource::{ExtractResource, ExtractResourcePlugin},
|
render::extract_resource::{ExtractResource, ExtractResourcePlugin},
|
||||||
};
|
};
|
||||||
use bevy_xr::session::{status_changed_to, XrStatus};
|
|
||||||
|
|
||||||
use crate::{init::OxrPreUpdateSet, resources::OxrSession};
|
use crate::{init::OxrSessionCreated, resources::OxrSession};
|
||||||
|
|
||||||
pub struct OxrReferenceSpacePlugin {
|
pub struct OxrReferenceSpacePlugin {
|
||||||
pub default_primary_ref_space: openxr::ReferenceSpaceType,
|
pub default_primary_ref_space: openxr::ReferenceSpaceType,
|
||||||
@@ -35,10 +34,8 @@ impl Plugin for OxrReferenceSpacePlugin {
|
|||||||
app.insert_resource(OxrPrimaryReferenceSpaceType(self.default_primary_ref_space));
|
app.insert_resource(OxrPrimaryReferenceSpaceType(self.default_primary_ref_space));
|
||||||
app.add_plugins(ExtractResourcePlugin::<OxrPrimaryReferenceSpace>::default());
|
app.add_plugins(ExtractResourcePlugin::<OxrPrimaryReferenceSpace>::default());
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
PreUpdate,
|
OxrSessionCreated,
|
||||||
set_primary_ref_space
|
set_primary_ref_space, // .in_set(OxrPreUpdateSet::UpdateCriticalComponents),
|
||||||
.run_if(status_changed_to(XrStatus::Ready))
|
|
||||||
.in_set(OxrPreUpdateSet::UpdateCriticalComponents),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,76 +4,142 @@ use bevy::{
|
|||||||
render::{
|
render::{
|
||||||
camera::{ManualTextureView, ManualTextureViewHandle, ManualTextureViews, RenderTarget},
|
camera::{ManualTextureView, ManualTextureViewHandle, ManualTextureViews, RenderTarget},
|
||||||
extract_resource::ExtractResourcePlugin,
|
extract_resource::ExtractResourcePlugin,
|
||||||
renderer::render_system,
|
pipelined_rendering::PipelinedRenderingPlugin,
|
||||||
view::ExtractedView,
|
view::ExtractedView,
|
||||||
Render, RenderApp, RenderSet,
|
Render, RenderApp, RenderSet,
|
||||||
},
|
},
|
||||||
transform::TransformSystem,
|
|
||||||
};
|
|
||||||
use bevy_xr::{
|
|
||||||
camera::{XrCamera, XrCameraBundle, XrProjection},
|
|
||||||
session::session_running,
|
|
||||||
};
|
};
|
||||||
|
use bevy_xr::camera::{XrCamera, XrCameraBundle, XrProjection};
|
||||||
use openxr::ViewStateFlags;
|
use openxr::ViewStateFlags;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
init::{session_started, OxrPreUpdateSet, OxrTrackingRoot},
|
init::{session_started, OxrHandleEvents, OxrLast, OxrTrackingRoot},
|
||||||
layer_builder::ProjectionLayer,
|
layer_builder::ProjectionLayer,
|
||||||
};
|
};
|
||||||
use crate::{reference_space::OxrPrimaryReferenceSpace, resources::*};
|
use crate::{reference_space::OxrPrimaryReferenceSpace, resources::*};
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, SystemSet)]
|
||||||
|
pub struct OxrRenderBegin;
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, SystemSet)]
|
||||||
|
pub struct OxrRenderEnd;
|
||||||
|
|
||||||
pub struct OxrRenderPlugin;
|
pub struct OxrRenderPlugin;
|
||||||
|
|
||||||
impl Plugin for OxrRenderPlugin {
|
impl Plugin for OxrRenderPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_plugins((ExtractResourcePlugin::<OxrViews>::default(),))
|
if app.is_plugin_added::<PipelinedRenderingPlugin>() {
|
||||||
|
app.init_resource::<Pipelined>();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.add_plugins((
|
||||||
|
ExtractResourcePlugin::<OxrFrameState>::default(),
|
||||||
|
ExtractResourcePlugin::<OxrRootTransform>::default(),
|
||||||
|
ExtractResourcePlugin::<OxrGraphicsInfo>::default(),
|
||||||
|
ExtractResourcePlugin::<OxrSwapchainImages>::default(),
|
||||||
|
ExtractResourcePlugin::<OxrViews>::default(),
|
||||||
|
))
|
||||||
.add_systems(
|
.add_systems(
|
||||||
PreUpdate,
|
PreUpdate,
|
||||||
(
|
|
||||||
init_views.run_if(resource_added::<OxrGraphicsInfo>),
|
|
||||||
locate_views.run_if(session_running),
|
|
||||||
update_views.run_if(session_running),
|
|
||||||
)
|
|
||||||
.chain()
|
|
||||||
.after(OxrPreUpdateSet::UpdateNonCriticalComponents),
|
|
||||||
)
|
|
||||||
.add_systems(
|
|
||||||
PostUpdate,
|
|
||||||
(locate_views, update_views)
|
(locate_views, update_views)
|
||||||
.chain()
|
.chain()
|
||||||
.run_if(session_running)
|
// .run_if(should_render)
|
||||||
.before(TransformSystem::TransformPropagate),
|
.run_if(session_started),
|
||||||
|
)
|
||||||
|
.add_systems(
|
||||||
|
OxrLast,
|
||||||
|
(
|
||||||
|
wait_frame.run_if(session_started),
|
||||||
|
update_cameras.run_if(session_started),
|
||||||
|
init_views.run_if(resource_added::<OxrSession>),
|
||||||
|
apply_deferred,
|
||||||
|
)
|
||||||
|
.chain()
|
||||||
|
.after(OxrHandleEvents),
|
||||||
|
)
|
||||||
|
.init_resource::<OxrViews>();
|
||||||
|
|
||||||
|
let render_app = app.sub_app_mut(RenderApp);
|
||||||
|
render_app
|
||||||
|
.configure_sets(
|
||||||
|
Render,
|
||||||
|
OxrRenderBegin
|
||||||
|
.after(RenderSet::ExtractCommands)
|
||||||
|
.before(RenderSet::PrepareAssets)
|
||||||
|
.before(RenderSet::ManageViews),
|
||||||
|
)
|
||||||
|
.configure_sets(
|
||||||
|
Render,
|
||||||
|
OxrRenderEnd
|
||||||
|
.after(RenderSet::Render)
|
||||||
|
.before(RenderSet::Cleanup),
|
||||||
)
|
)
|
||||||
.add_systems(Last, wait_frame.run_if(session_started));
|
|
||||||
app.sub_app_mut(RenderApp)
|
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Render,
|
Render,
|
||||||
(
|
(
|
||||||
(
|
begin_frame,
|
||||||
insert_texture_views,
|
insert_texture_views,
|
||||||
locate_views.run_if(resource_exists::<OxrPrimaryReferenceSpace>),
|
locate_views,
|
||||||
update_views_render_world,
|
update_views_render_world,
|
||||||
|
wait_image,
|
||||||
)
|
)
|
||||||
.chain()
|
.chain()
|
||||||
.in_set(RenderSet::PrepareAssets),
|
.run_if(session_started)
|
||||||
begin_frame
|
.in_set(OxrRenderBegin),
|
||||||
.before(RenderSet::Queue)
|
)
|
||||||
.before(insert_texture_views),
|
.add_systems(
|
||||||
wait_image.in_set(RenderSet::Render).before(render_system),
|
Render,
|
||||||
(release_image, end_frame)
|
(release_image, end_frame)
|
||||||
.chain()
|
.chain()
|
||||||
.in_set(RenderSet::Cleanup),
|
.run_if(session_started)
|
||||||
)
|
.in_set(OxrRenderEnd),
|
||||||
.run_if(session_started),
|
|
||||||
)
|
)
|
||||||
.insert_resource(OxrRenderLayers(vec![Box::new(ProjectionLayer)]));
|
.insert_resource(OxrRenderLayers(vec![Box::new(ProjectionLayer)]));
|
||||||
|
|
||||||
|
// app.add_systems(
|
||||||
|
// PreUpdate,
|
||||||
|
// (
|
||||||
|
// init_views.run_if(resource_added::<OxrGraphicsInfo>),
|
||||||
|
// locate_views.run_if(session_running),
|
||||||
|
// update_views.run_if(session_running),
|
||||||
|
// )
|
||||||
|
// .chain(), // .after(OxrPreUpdateSet::UpdateNonCriticalComponents),
|
||||||
|
// )
|
||||||
|
// .add_systems(
|
||||||
|
// PostUpdate,
|
||||||
|
// (locate_views, update_views)
|
||||||
|
// .chain()
|
||||||
|
// .run_if(session_running)
|
||||||
|
// .before(TransformSystem::TransformPropagate),
|
||||||
|
// )
|
||||||
|
// .add_systems(Last, wait_frame.run_if(session_started));
|
||||||
|
// app.sub_app_mut(RenderApp)
|
||||||
|
// .add_systems(
|
||||||
|
// Render,
|
||||||
|
// (
|
||||||
|
// (
|
||||||
|
// insert_texture_views,
|
||||||
|
// locate_views.run_if(resource_exists::<OxrPrimaryReferenceSpace>),
|
||||||
|
// update_views_render_world,
|
||||||
|
// )
|
||||||
|
// .chain()
|
||||||
|
// .in_set(RenderSet::PrepareAssets),
|
||||||
|
// begin_frame
|
||||||
|
// .before(RenderSet::Queue)
|
||||||
|
// .before(insert_texture_views),
|
||||||
|
// wait_image.in_set(RenderSet::Render).before(render_system),
|
||||||
|
// (release_image, end_frame)
|
||||||
|
// .chain()
|
||||||
|
// .in_set(RenderSet::Cleanup),
|
||||||
|
// )
|
||||||
|
// .run_if(session_started),
|
||||||
|
// )
|
||||||
|
// .insert_resource(OxrRenderLayers(vec![Box::new(ProjectionLayer)]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const XR_TEXTURE_INDEX: u32 = 3383858418;
|
pub const XR_TEXTURE_INDEX: u32 = 3383858418;
|
||||||
|
|
||||||
// TODO: have cameras initialized externally and then recieved by this function.
|
|
||||||
/// This is needed to properly initialize the texture views so that bevy will set them to the correct resolution despite them being updated in the render world.
|
|
||||||
pub fn init_views(
|
pub fn init_views(
|
||||||
graphics_info: Res<OxrGraphicsInfo>,
|
graphics_info: Res<OxrGraphicsInfo>,
|
||||||
mut manual_texture_views: ResMut<ManualTextureViews>,
|
mut manual_texture_views: ResMut<ManualTextureViews>,
|
||||||
@@ -84,7 +150,6 @@ pub fn init_views(
|
|||||||
let _span = info_span!("xr_init_views");
|
let _span = info_span!("xr_init_views");
|
||||||
let temp_tex = swapchain_images.first().unwrap();
|
let temp_tex = swapchain_images.first().unwrap();
|
||||||
// this for loop is to easily add support for quad or mono views in the future.
|
// this for loop is to easily add support for quad or mono views in the future.
|
||||||
let mut views = Vec::with_capacity(2);
|
|
||||||
for index in 0..2 {
|
for index in 0..2 {
|
||||||
info!("{}", graphics_info.resolution);
|
info!("{}", graphics_info.resolution);
|
||||||
let view_handle =
|
let view_handle =
|
||||||
@@ -115,10 +180,7 @@ pub fn init_views(
|
|||||||
warn!("Multiple OxrTrackingRoots! this is not allowed");
|
warn!("Multiple OxrTrackingRoots! this is not allowed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
views.push(default());
|
|
||||||
}
|
}
|
||||||
commands.insert_resource(OxrViews(views));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait_frame(mut frame_waiter: ResMut<OxrFrameWaiter>, mut commands: Commands) {
|
pub fn wait_frame(mut frame_waiter: ResMut<OxrFrameWaiter>, mut commands: Commands) {
|
||||||
@@ -126,26 +188,43 @@ pub fn wait_frame(mut frame_waiter: ResMut<OxrFrameWaiter>, mut commands: Comman
|
|||||||
let state = frame_waiter.wait().expect("Failed to wait frame");
|
let state = frame_waiter.wait().expect("Failed to wait frame");
|
||||||
// Here we insert the predicted display time for when this frame will be displayed.
|
// Here we insert the predicted display time for when this frame will be displayed.
|
||||||
// TODO: don't add predicted_display_period if pipelined rendering plugin not enabled
|
// TODO: don't add predicted_display_period if pipelined rendering plugin not enabled
|
||||||
commands.insert_resource(OxrTime(state.predicted_display_time));
|
commands.insert_resource(OxrFrameState(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_cameras(
|
||||||
|
frame_state: Res<OxrFrameState>,
|
||||||
|
mut cameras: Query<&mut Camera, With<XrCamera>>,
|
||||||
|
) {
|
||||||
|
if frame_state.is_changed() {
|
||||||
|
for mut camera in &mut cameras {
|
||||||
|
camera.is_active = frame_state.should_render
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn locate_views(
|
pub fn locate_views(
|
||||||
session: Res<OxrSession>,
|
session: Res<OxrSession>,
|
||||||
ref_space: Res<OxrPrimaryReferenceSpace>,
|
ref_space: Res<OxrPrimaryReferenceSpace>,
|
||||||
time: Res<OxrTime>,
|
frame_state: Res<OxrFrameState>,
|
||||||
mut openxr_views: ResMut<OxrViews>,
|
mut openxr_views: ResMut<OxrViews>,
|
||||||
|
pipelined: Option<Res<Pipelined>>,
|
||||||
) {
|
) {
|
||||||
let _span = info_span!("xr_locate_views");
|
let time = if pipelined.is_some() {
|
||||||
|
openxr::Time::from_nanos(
|
||||||
|
frame_state.predicted_display_time.as_nanos()
|
||||||
|
+ frame_state.predicted_display_period.as_nanos(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
frame_state.predicted_display_time
|
||||||
|
};
|
||||||
let (flags, xr_views) = session
|
let (flags, xr_views) = session
|
||||||
.locate_views(
|
.locate_views(
|
||||||
openxr::ViewConfigurationType::PRIMARY_STEREO,
|
openxr::ViewConfigurationType::PRIMARY_STEREO,
|
||||||
**time,
|
time,
|
||||||
&ref_space,
|
&ref_space,
|
||||||
)
|
)
|
||||||
.expect("Failed to locate views");
|
.expect("Failed to locate views");
|
||||||
if openxr_views.len() != xr_views.len() {
|
|
||||||
openxr_views.resize(xr_views.len(), default());
|
|
||||||
}
|
|
||||||
match (
|
match (
|
||||||
flags & ViewStateFlags::ORIENTATION_VALID == ViewStateFlags::ORIENTATION_VALID,
|
flags & ViewStateFlags::ORIENTATION_VALID == ViewStateFlags::ORIENTATION_VALID,
|
||||||
flags & ViewStateFlags::POSITION_VALID == ViewStateFlags::POSITION_VALID,
|
flags & ViewStateFlags::POSITION_VALID == ViewStateFlags::POSITION_VALID,
|
||||||
@@ -153,12 +232,18 @@ pub fn locate_views(
|
|||||||
(true, true) => *openxr_views = OxrViews(xr_views),
|
(true, true) => *openxr_views = OxrViews(xr_views),
|
||||||
(true, false) => {
|
(true, false) => {
|
||||||
for (i, view) in openxr_views.iter_mut().enumerate() {
|
for (i, view) in openxr_views.iter_mut().enumerate() {
|
||||||
view.pose.orientation = xr_views[i].pose.orientation;
|
let Some(xr_view) = xr_views.get(i) else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
view.pose.orientation = xr_view.pose.orientation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(false, true) => {
|
(false, true) => {
|
||||||
for (i, view) in openxr_views.iter_mut().enumerate() {
|
for (i, view) in openxr_views.iter_mut().enumerate() {
|
||||||
view.pose.position = xr_views[i].pose.position;
|
let Some(xr_view) = xr_views.get(i) else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
view.pose.position = xr_view.pose.position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(false, false) => {}
|
(false, false) => {}
|
||||||
@@ -372,64 +457,21 @@ pub fn end_frame(world: &mut World) {
|
|||||||
}
|
}
|
||||||
world.resource_scope::<OxrFrameStream, ()>(|world, mut frame_stream| {
|
world.resource_scope::<OxrFrameStream, ()>(|world, mut frame_stream| {
|
||||||
let mut layers = vec![];
|
let mut layers = vec![];
|
||||||
|
let frame_state = world.resource::<OxrFrameState>();
|
||||||
|
if frame_state.should_render {
|
||||||
for layer in world.resource::<OxrRenderLayers>().iter() {
|
for layer in world.resource::<OxrRenderLayers>().iter() {
|
||||||
layers.push(layer.get(world));
|
if let Some(layer) = layer.get(world) {
|
||||||
|
layers.push(layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let layers: Vec<_> = layers.iter().map(Box::as_ref).collect();
|
let layers: Vec<_> = layers.iter().map(Box::as_ref).collect();
|
||||||
frame_stream
|
frame_stream
|
||||||
.end(
|
.end(
|
||||||
**world.resource::<OxrTime>(),
|
frame_state.predicted_display_time,
|
||||||
world.resource::<OxrGraphicsInfo>().blend_mode,
|
world.resource::<OxrGraphicsInfo>().blend_mode,
|
||||||
&layers,
|
&layers,
|
||||||
)
|
)
|
||||||
.expect("Failed to end frame");
|
.expect("Failed to end frame");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn end_frame(
|
|
||||||
// mut frame_stream: ResMut<OxrFrameStream>,
|
|
||||||
// mut swapchain: ResMut<OxrSwapchain>,
|
|
||||||
// stage: Res<OxrStage>,
|
|
||||||
// display_time: Res<OxrTime>,
|
|
||||||
// graphics_info: Res<OxrGraphicsInfo>,
|
|
||||||
// openxr_views: Res<OxrViews>,
|
|
||||||
// ) {
|
|
||||||
// let _span = info_span!("xr_end_frame");
|
|
||||||
// swapchain.release_image().unwrap();
|
|
||||||
// let rect = openxr::Rect2Di {
|
|
||||||
// offset: openxr::Offset2Di { x: 0, y: 0 },
|
|
||||||
// extent: openxr::Extent2Di {
|
|
||||||
// width: graphics_info.resolution.x as _,
|
|
||||||
// height: graphics_info.resolution.y as _,
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
// frame_stream
|
|
||||||
// .end(
|
|
||||||
// **display_time,
|
|
||||||
// graphics_info.blend_mode,
|
|
||||||
// &[&CompositionLayerProjection::new()
|
|
||||||
// .layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
|
|
||||||
// .space(&stage)
|
|
||||||
// .views(&[
|
|
||||||
// CompositionLayerProjectionView::new()
|
|
||||||
// .pose(openxr_views.0[0].pose)
|
|
||||||
// .fov(openxr_views.0[0].fov)
|
|
||||||
// .sub_image(
|
|
||||||
// SwapchainSubImage::new()
|
|
||||||
// .swapchain(&swapchain)
|
|
||||||
// .image_array_index(0)
|
|
||||||
// .image_rect(rect),
|
|
||||||
// ),
|
|
||||||
// CompositionLayerProjectionView::new()
|
|
||||||
// .pose(openxr_views.0[1].pose)
|
|
||||||
// .fov(openxr_views.0[1].fov)
|
|
||||||
// .sub_image(
|
|
||||||
// SwapchainSubImage::new()
|
|
||||||
// .swapchain(&swapchain)
|
|
||||||
// .image_array_index(1)
|
|
||||||
// .image_rect(rect),
|
|
||||||
// ),
|
|
||||||
// ])],
|
|
||||||
// )
|
|
||||||
// .expect("Failed to end frame");
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -373,7 +373,7 @@ impl OxrSwapchain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Stores the generated swapchain images.
|
/// Stores the generated swapchain images.
|
||||||
#[derive(Debug, Deref, Resource, Clone)]
|
#[derive(Debug, Deref, Resource, ExtractResource, Clone)]
|
||||||
pub struct OxrSwapchainImages(pub Arc<Vec<wgpu::Texture>>);
|
pub struct OxrSwapchainImages(pub Arc<Vec<wgpu::Texture>>);
|
||||||
|
|
||||||
/// Thread safe wrapper around [openxr::Space] representing the stage.
|
/// Thread safe wrapper around [openxr::Space] representing the stage.
|
||||||
@@ -381,7 +381,7 @@ pub struct OxrSwapchainImages(pub Arc<Vec<wgpu::Texture>>);
|
|||||||
// pub struct OxrStage(pub Arc<openxr::Space>);
|
// pub struct OxrStage(pub Arc<openxr::Space>);
|
||||||
|
|
||||||
/// Stores the latest generated [OxrViews]
|
/// Stores the latest generated [OxrViews]
|
||||||
#[derive(Clone, Resource, ExtractResource, Deref, DerefMut)]
|
#[derive(Clone, Resource, ExtractResource, Deref, DerefMut, Default)]
|
||||||
pub struct OxrViews(pub Vec<openxr::View>);
|
pub struct OxrViews(pub Vec<openxr::View>);
|
||||||
|
|
||||||
/// Wrapper around [openxr::SystemId] to allow it to be stored as a resource.
|
/// Wrapper around [openxr::SystemId] to allow it to be stored as a resource.
|
||||||
@@ -420,7 +420,7 @@ pub struct OxrPassthroughLayer(pub openxr::PassthroughLayer);
|
|||||||
pub struct OxrRenderLayers(pub Vec<Box<dyn LayerProvider + Send + Sync>>);
|
pub struct OxrRenderLayers(pub Vec<Box<dyn LayerProvider + Send + Sync>>);
|
||||||
|
|
||||||
/// Resource storing graphics info for the currently running session.
|
/// Resource storing graphics info for the currently running session.
|
||||||
#[derive(Clone, Copy, Resource)]
|
#[derive(Clone, Copy, Resource, ExtractResource)]
|
||||||
pub struct OxrGraphicsInfo {
|
pub struct OxrGraphicsInfo {
|
||||||
pub blend_mode: EnvironmentBlendMode,
|
pub blend_mode: EnvironmentBlendMode,
|
||||||
pub resolution: UVec2,
|
pub resolution: UVec2,
|
||||||
@@ -453,9 +453,9 @@ impl OxrSessionStarted {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The calculated display time for the app. Passed through the pipeline.
|
/// The frame state returned from [FrameWaiter::wait_frame](openxr::FrameWaiter::wait)
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Deref, DerefMut, Resource, ExtractResource)]
|
#[derive(Clone, Deref, DerefMut, Resource, ExtractResource)]
|
||||||
pub struct OxrTime(pub openxr::Time);
|
pub struct OxrFrameState(pub openxr::FrameState);
|
||||||
|
|
||||||
/// The root transform's global position for late latching in the render world.
|
/// The root transform's global position for late latching in the render world.
|
||||||
#[derive(ExtractResource, Resource, Clone, Copy, Default)]
|
#[derive(ExtractResource, Resource, Clone, Copy, Default)]
|
||||||
@@ -464,3 +464,7 @@ pub struct OxrRootTransform(pub GlobalTransform);
|
|||||||
#[derive(ExtractResource, Resource, Clone, Copy, Default, Deref, DerefMut, PartialEq)]
|
#[derive(ExtractResource, Resource, Clone, Copy, Default, Deref, DerefMut, PartialEq)]
|
||||||
/// This is inserted into the world to signify if the session should be cleaned up.
|
/// This is inserted into the world to signify if the session should be cleaned up.
|
||||||
pub struct OxrCleanupSession(pub bool);
|
pub struct OxrCleanupSession(pub bool);
|
||||||
|
|
||||||
|
/// Instructs systems to add display period
|
||||||
|
#[derive(Clone, Copy, Default, Resource)]
|
||||||
|
pub struct Pipelined;
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
use std::sync::{Arc, RwLock};
|
use bevy::{prelude::*, render::extract_resource::ExtractResource};
|
||||||
|
|
||||||
use bevy::prelude::*;
|
|
||||||
|
|
||||||
pub struct XrSessionPlugin;
|
pub struct XrSessionPlugin;
|
||||||
|
|
||||||
@@ -13,7 +11,7 @@ impl Plugin for XrSessionPlugin {
|
|||||||
.add_event::<XrStatusChanged>()
|
.add_event::<XrStatusChanged>()
|
||||||
.add_systems(
|
.add_systems(
|
||||||
PreUpdate,
|
PreUpdate,
|
||||||
handle_session.run_if(resource_exists::<XrSharedStatus>),
|
handle_session.run_if(resource_exists::<XrStatus>),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,24 +19,7 @@ impl Plugin for XrSessionPlugin {
|
|||||||
#[derive(Event, Clone, Copy, Deref)]
|
#[derive(Event, Clone, Copy, Deref)]
|
||||||
pub struct XrStatusChanged(pub XrStatus);
|
pub struct XrStatusChanged(pub XrStatus);
|
||||||
|
|
||||||
#[derive(Resource, Clone)]
|
#[derive(Clone, Copy, Debug, ExtractResource, Resource, PartialEq, Eq)]
|
||||||
pub struct XrSharedStatus(Arc<RwLock<XrStatus>>);
|
|
||||||
|
|
||||||
impl XrSharedStatus {
|
|
||||||
pub fn new(status: XrStatus) -> Self {
|
|
||||||
Self(Arc::new(RwLock::new(status)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self) -> XrStatus {
|
|
||||||
*self.0.read().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set(&self, status: XrStatus) {
|
|
||||||
*self.0.write().unwrap() = status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum XrStatus {
|
pub enum XrStatus {
|
||||||
/// An XR session is not available here
|
/// An XR session is not available here
|
||||||
@@ -58,16 +39,15 @@ pub enum XrStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_session(
|
pub fn handle_session(
|
||||||
status: Res<XrSharedStatus>,
|
current_status: Res<XrStatus>,
|
||||||
mut previous_status: Local<Option<XrStatus>>,
|
mut previous_status: Local<Option<XrStatus>>,
|
||||||
mut create_session: EventWriter<CreateXrSession>,
|
mut create_session: EventWriter<CreateXrSession>,
|
||||||
mut begin_session: EventWriter<BeginXrSession>,
|
mut begin_session: EventWriter<BeginXrSession>,
|
||||||
mut end_session: EventWriter<EndXrSession>,
|
mut end_session: EventWriter<EndXrSession>,
|
||||||
mut destroy_session: EventWriter<DestroyXrSession>,
|
mut destroy_session: EventWriter<DestroyXrSession>,
|
||||||
) {
|
) {
|
||||||
let current_status = status.get();
|
if *previous_status != Some(*current_status) {
|
||||||
if *previous_status != Some(current_status) {
|
match *current_status {
|
||||||
match current_status {
|
|
||||||
XrStatus::Unavailable => {}
|
XrStatus::Unavailable => {}
|
||||||
XrStatus::Available => {
|
XrStatus::Available => {
|
||||||
create_session.send_default();
|
create_session.send_default();
|
||||||
@@ -85,7 +65,7 @@ pub fn handle_session(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*previous_status = Some(current_status);
|
*previous_status = Some(*current_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A [`Condition`](bevy::ecs::schedule::Condition) that allows the system to run when the xr status changed to a specific [`XrStatus`].
|
/// A [`Condition`](bevy::ecs::schedule::Condition) that allows the system to run when the xr status changed to a specific [`XrStatus`].
|
||||||
@@ -98,29 +78,23 @@ pub fn status_changed_to(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A [`Condition`](bevy::ecs::schedule::Condition) system that says if the XR session is available. Returns true as long as [`XrStatus`] exists and isn't [`Unavailable`](XrStatus::Unavailable).
|
/// A [`Condition`](bevy::ecs::schedule::Condition) system that says if the XR session is available. Returns true as long as [`XrStatus`] exists and isn't [`Unavailable`](XrStatus::Unavailable).
|
||||||
pub fn session_available(status: Option<Res<XrSharedStatus>>) -> bool {
|
pub fn session_available(status: Option<Res<XrStatus>>) -> bool {
|
||||||
status.is_some_and(|s| s.get() != XrStatus::Unavailable)
|
status.is_some_and(|s| *s != XrStatus::Unavailable)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A [`Condition`](bevy::ecs::schedule::Condition) system that says if the XR session is ready or running
|
/// A [`Condition`](bevy::ecs::schedule::Condition) system that says if the XR session is ready or running
|
||||||
pub fn session_created(status: Option<Res<XrSharedStatus>>) -> bool {
|
pub fn session_ready_or_running(status: Option<Res<XrStatus>>) -> bool {
|
||||||
matches!(
|
matches!(status.as_deref(), Some(XrStatus::Ready | XrStatus::Running))
|
||||||
status.as_deref().map(XrSharedStatus::get),
|
|
||||||
Some(XrStatus::Ready | XrStatus::Running)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A [`Condition`](bevy::ecs::schedule::Condition) system that says if the XR session is running
|
/// A [`Condition`](bevy::ecs::schedule::Condition) system that says if the XR session is running
|
||||||
pub fn session_running(status: Option<Res<XrSharedStatus>>) -> bool {
|
pub fn session_running(status: Option<Res<XrStatus>>) -> bool {
|
||||||
matches!(
|
matches!(status.as_deref(), Some(XrStatus::Running))
|
||||||
status.as_deref().map(XrSharedStatus::get),
|
|
||||||
Some(XrStatus::Running)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A function that returns a [`Condition`](bevy::ecs::schedule::Condition) system that says if an the [`XrStatus`] is in a specific state
|
/// A function that returns a [`Condition`](bevy::ecs::schedule::Condition) system that says if an the [`XrStatus`] is in a specific state
|
||||||
pub fn status_equals(status: XrStatus) -> impl FnMut(Option<Res<XrSharedStatus>>) -> bool {
|
pub fn status_equals(status: XrStatus) -> impl FnMut(Option<Res<XrStatus>>) -> bool {
|
||||||
move |state: Option<Res<XrSharedStatus>>| state.is_some_and(|s| s.get() == status)
|
move |state: Option<Res<XrStatus>>| state.is_some_and(|s| *s == status)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event sent to backends to create an XR session. Should only be called in the [`XrStatus::Available`] state.
|
/// Event sent to backends to create an XR session. Should only be called in the [`XrStatus::Available`] state.
|
||||||
|
|||||||
Reference in New Issue
Block a user