small changes
This commit is contained in:
305
src/lib.rs
305
src/lib.rs
@@ -2,11 +2,18 @@ use std::sync::Arc;
|
||||
|
||||
use bevy::{
|
||||
app::PluginGroupBuilder,
|
||||
core_pipeline::tonemapping::{DebandDither, Tonemapping},
|
||||
math::Vec3A,
|
||||
prelude::*,
|
||||
render::{
|
||||
camera::{ManualTextureView, ManualTextureViewHandle, ManualTextureViews},
|
||||
camera::{
|
||||
CameraProjection, CameraProjectionPlugin, CameraRenderGraph, ManualTextureView,
|
||||
ManualTextureViewHandle, ManualTextureViews, RenderTarget,
|
||||
},
|
||||
pipelined_rendering::PipelinedRenderingPlugin,
|
||||
primitives::Frustum,
|
||||
renderer::{render_system, RenderAdapter, RenderAdapterInfo, RenderInstance, RenderQueue},
|
||||
view::{ColorGrading, VisibleEntities},
|
||||
Render, RenderApp, RenderPlugin,
|
||||
},
|
||||
window::PresentMode,
|
||||
@@ -32,43 +39,299 @@ impl Plugin for XrPlugin {
|
||||
let (device, queue, adapter_info, adapter, instance) =
|
||||
session.get_render_resources().unwrap();
|
||||
|
||||
app.insert_non_send_resource(session.clone());
|
||||
app.add_plugins(RenderPlugin {
|
||||
render_creation: bevy::render::settings::RenderCreation::Manual(
|
||||
device.into(),
|
||||
RenderQueue(Arc::new(queue)),
|
||||
RenderAdapterInfo(adapter_info),
|
||||
RenderAdapter(Arc::new(adapter)),
|
||||
RenderInstance(Arc::new(instance)),
|
||||
),
|
||||
});
|
||||
let input = session.create_input(Bindings::OculusTouch).unwrap();
|
||||
|
||||
app.add_systems(Last, begin_frame);
|
||||
let left_primary_button = input
|
||||
.create_action(input::hand_left::PrimaryButton::CLICK)
|
||||
.unwrap();
|
||||
|
||||
let left_hand_pose = input.create_action(input::hand_left::Grip::POSE).unwrap();
|
||||
|
||||
app.insert_non_send_resource(left_primary_button);
|
||||
app.insert_non_send_resource(left_hand_pose);
|
||||
app.insert_non_send_resource(session.clone());
|
||||
app.add_plugins((
|
||||
RenderPlugin {
|
||||
render_creation: bevy::render::settings::RenderCreation::Manual(
|
||||
device.into(),
|
||||
RenderQueue(Arc::new(queue)),
|
||||
RenderAdapterInfo(adapter_info),
|
||||
RenderAdapter(Arc::new(adapter)),
|
||||
RenderInstance(Arc::new(instance)),
|
||||
),
|
||||
},
|
||||
CameraProjectionPlugin::<XRProjection>::default(),
|
||||
));
|
||||
|
||||
app.add_systems(PreUpdate, begin_frame);
|
||||
app.add_systems(Last, locate_views);
|
||||
app.add_systems(Startup, setup);
|
||||
let render_app = app.sub_app_mut(RenderApp);
|
||||
render_app.insert_non_send_resource(session);
|
||||
render_app.add_systems(Render, end_frame.after(render_system));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn begin_frame(
|
||||
#[derive(Bundle)]
|
||||
pub struct XrCameraBundle {
|
||||
pub camera: Camera,
|
||||
pub camera_render_graph: CameraRenderGraph,
|
||||
pub xr_projection: PerspectiveProjection,
|
||||
pub visible_entities: VisibleEntities,
|
||||
pub frustum: Frustum,
|
||||
pub transform: Transform,
|
||||
pub global_transform: GlobalTransform,
|
||||
pub camera_3d: Camera3d,
|
||||
pub tonemapping: Tonemapping,
|
||||
pub dither: DebandDither,
|
||||
pub color_grading: ColorGrading,
|
||||
pub xr_camera_type: XrCameraType,
|
||||
}
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Component)]
|
||||
pub enum XrCameraType {
|
||||
Xr(Eye),
|
||||
Flatscreen,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||
pub enum Eye {
|
||||
Left = 0,
|
||||
Right = 1,
|
||||
}
|
||||
|
||||
impl XrCameraBundle {
|
||||
pub fn new(eye: Eye) -> Self {
|
||||
Self {
|
||||
camera: Camera {
|
||||
order: -1,
|
||||
target: RenderTarget::TextureView(match eye {
|
||||
Eye::Left => LEFT_XR_TEXTURE_HANDLE,
|
||||
Eye::Right => RIGHT_XR_TEXTURE_HANDLE,
|
||||
}),
|
||||
viewport: None,
|
||||
..default()
|
||||
},
|
||||
camera_render_graph: CameraRenderGraph::new(bevy::core_pipeline::core_3d::graph::NAME),
|
||||
xr_projection: Default::default(),
|
||||
visible_entities: Default::default(),
|
||||
frustum: Default::default(),
|
||||
transform: Default::default(),
|
||||
global_transform: Default::default(),
|
||||
camera_3d: Default::default(),
|
||||
tonemapping: Default::default(),
|
||||
dither: DebandDither::Enabled,
|
||||
color_grading: Default::default(),
|
||||
xr_camera_type: XrCameraType::Xr(eye),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Component, Reflect)]
|
||||
#[reflect(Component, Default)]
|
||||
pub struct XRProjection {
|
||||
pub near: f32,
|
||||
pub far: f32,
|
||||
#[reflect(ignore)]
|
||||
pub fov: Fov,
|
||||
}
|
||||
|
||||
impl Default for XRProjection {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
near: 0.1,
|
||||
far: 1000.,
|
||||
fov: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CameraProjection for XRProjection {
|
||||
// =============================================================================
|
||||
// math code adapted from
|
||||
// https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/master/src/common/xr_linear.h
|
||||
// Copyright (c) 2017 The Khronos Group Inc.
|
||||
// Copyright (c) 2016 Oculus VR, LLC.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// =============================================================================
|
||||
fn get_projection_matrix(&self) -> Mat4 {
|
||||
// symmetric perspective for debugging
|
||||
// let x_fov = (self.fov.angle_left.abs() + self.fov.angle_right.abs());
|
||||
// let y_fov = (self.fov.angle_up.abs() + self.fov.angle_down.abs());
|
||||
// return Mat4::perspective_infinite_reverse_rh(y_fov, x_fov / y_fov, self.near);
|
||||
|
||||
let fov = self.fov;
|
||||
let is_vulkan_api = false; // FIXME wgpu probably abstracts this
|
||||
let near_z = self.near;
|
||||
let far_z = -1.; // use infinite proj
|
||||
// let far_z = self.far;
|
||||
|
||||
let tan_angle_left = fov.angle_left.tan();
|
||||
let tan_angle_right = fov.angle_right.tan();
|
||||
|
||||
let tan_angle_down = fov.angle_down.tan();
|
||||
let tan_angle_up = fov.angle_up.tan();
|
||||
|
||||
let tan_angle_width = tan_angle_right - tan_angle_left;
|
||||
|
||||
// Set to tanAngleDown - tanAngleUp for a clip space with positive Y
|
||||
// down (Vulkan). Set to tanAngleUp - tanAngleDown for a clip space with
|
||||
// positive Y up (OpenGL / D3D / Metal).
|
||||
// const float tanAngleHeight =
|
||||
// graphicsApi == GRAPHICS_VULKAN ? (tanAngleDown - tanAngleUp) : (tanAngleUp - tanAngleDown);
|
||||
let tan_angle_height = if is_vulkan_api {
|
||||
tan_angle_down - tan_angle_up
|
||||
} else {
|
||||
tan_angle_up - tan_angle_down
|
||||
};
|
||||
|
||||
// Set to nearZ for a [-1,1] Z clip space (OpenGL / OpenGL ES).
|
||||
// Set to zero for a [0,1] Z clip space (Vulkan / D3D / Metal).
|
||||
// const float offsetZ =
|
||||
// (graphicsApi == GRAPHICS_OPENGL || graphicsApi == GRAPHICS_OPENGL_ES) ? nearZ : 0;
|
||||
// FIXME handle enum of graphics apis
|
||||
let offset_z = 0.;
|
||||
|
||||
let mut cols: [f32; 16] = [0.0; 16];
|
||||
|
||||
if far_z <= near_z {
|
||||
// place the far plane at infinity
|
||||
cols[0] = 2. / tan_angle_width;
|
||||
cols[4] = 0.;
|
||||
cols[8] = (tan_angle_right + tan_angle_left) / tan_angle_width;
|
||||
cols[12] = 0.;
|
||||
|
||||
cols[1] = 0.;
|
||||
cols[5] = 2. / tan_angle_height;
|
||||
cols[9] = (tan_angle_up + tan_angle_down) / tan_angle_height;
|
||||
cols[13] = 0.;
|
||||
|
||||
cols[2] = 0.;
|
||||
cols[6] = 0.;
|
||||
cols[10] = -1.;
|
||||
cols[14] = -(near_z + offset_z);
|
||||
|
||||
cols[3] = 0.;
|
||||
cols[7] = 0.;
|
||||
cols[11] = -1.;
|
||||
cols[15] = 0.;
|
||||
|
||||
// bevy uses the _reverse_ infinite projection
|
||||
// https://dev.theomader.com/depth-precision/
|
||||
let z_reversal = Mat4::from_cols_array_2d(&[
|
||||
[1f32, 0., 0., 0.],
|
||||
[0., 1., 0., 0.],
|
||||
[0., 0., -1., 0.],
|
||||
[0., 0., 1., 1.],
|
||||
]);
|
||||
|
||||
return z_reversal * Mat4::from_cols_array(&cols);
|
||||
} else {
|
||||
// normal projection
|
||||
cols[0] = 2. / tan_angle_width;
|
||||
cols[4] = 0.;
|
||||
cols[8] = (tan_angle_right + tan_angle_left) / tan_angle_width;
|
||||
cols[12] = 0.;
|
||||
|
||||
cols[1] = 0.;
|
||||
cols[5] = 2. / tan_angle_height;
|
||||
cols[9] = (tan_angle_up + tan_angle_down) / tan_angle_height;
|
||||
cols[13] = 0.;
|
||||
|
||||
cols[2] = 0.;
|
||||
cols[6] = 0.;
|
||||
cols[10] = -(far_z + offset_z) / (far_z - near_z);
|
||||
cols[14] = -(far_z * (near_z + offset_z)) / (far_z - near_z);
|
||||
|
||||
cols[3] = 0.;
|
||||
cols[7] = 0.;
|
||||
cols[11] = -1.;
|
||||
cols[15] = 0.;
|
||||
}
|
||||
|
||||
Mat4::from_cols_array(&cols)
|
||||
}
|
||||
|
||||
fn update(&mut self, _width: f32, _height: f32) {}
|
||||
|
||||
fn far(&self) -> f32 {
|
||||
self.far
|
||||
}
|
||||
|
||||
fn get_frustum_corners(&self, z_near: f32, z_far: f32) -> [Vec3A; 8] {
|
||||
let tan_angle_left = self.fov.angle_left.tan();
|
||||
let tan_angle_right = self.fov.angle_right.tan();
|
||||
|
||||
let tan_angle_bottom = self.fov.angle_down.tan();
|
||||
let tan_angle_top = self.fov.angle_up.tan();
|
||||
|
||||
// NOTE: These vertices are in the specific order required by [`calculate_cascade`].
|
||||
[
|
||||
Vec3A::new(tan_angle_right, tan_angle_bottom, 1.0) * z_near, // bottom right
|
||||
Vec3A::new(tan_angle_right, tan_angle_top, 1.0) * z_near, // top right
|
||||
Vec3A::new(tan_angle_left, tan_angle_top, 1.0) * z_near, // top left
|
||||
Vec3A::new(tan_angle_left, tan_angle_bottom, 1.0) * z_near, // bottom left
|
||||
Vec3A::new(tan_angle_right, tan_angle_bottom, 1.0) * z_far, // bottom right
|
||||
Vec3A::new(tan_angle_right, tan_angle_top, 1.0) * z_far, // top right
|
||||
Vec3A::new(tan_angle_left, tan_angle_top, 1.0) * z_far, // top left
|
||||
Vec3A::new(tan_angle_left, tan_angle_bottom, 1.0) * z_far, // bottom left
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct Cameras(Entity, Entity);
|
||||
|
||||
fn setup(mut commands: Commands) {
|
||||
let left = commands.spawn(XrCameraBundle::new(Eye::Left)).id();
|
||||
let right = commands.spawn(XrCameraBundle::new(Eye::Right)).id();
|
||||
commands.insert_resource(Cameras(left, right));
|
||||
}
|
||||
|
||||
pub fn begin_frame(session: NonSend<Session>, action: NonSend<Action<Pose>>) {
|
||||
session.begin_frame().unwrap();
|
||||
}
|
||||
|
||||
fn locate_views(
|
||||
session: NonSend<Session>,
|
||||
mut manual_texture_views: ResMut<ManualTextureViews>,
|
||||
cameras: Res<Cameras>,
|
||||
mut transforms: Query<(&mut Transform)>,
|
||||
) {
|
||||
let (left, right) = session.begin_frame().unwrap();
|
||||
let (left_view, right_view) = session.locate_views().unwrap();
|
||||
|
||||
let left = ManualTextureView {
|
||||
texture_view: left.texture_view().unwrap().into(),
|
||||
size: left.resolution(),
|
||||
format: left.format(),
|
||||
texture_view: left_view.texture_view().unwrap().into(),
|
||||
size: left_view.resolution(),
|
||||
format: left_view.format(),
|
||||
};
|
||||
let right = ManualTextureView {
|
||||
texture_view: right.texture_view().unwrap().into(),
|
||||
size: right.resolution(),
|
||||
format: right.format(),
|
||||
texture_view: right_view.texture_view().unwrap().into(),
|
||||
size: right_view.resolution(),
|
||||
format: right_view.format(),
|
||||
};
|
||||
|
||||
manual_texture_views.insert(LEFT_XR_TEXTURE_HANDLE, left);
|
||||
if let Ok(mut transform) = transforms.get_mut(cameras.0) {
|
||||
let Pose {
|
||||
translation,
|
||||
rotation,
|
||||
} = left_view.pose();
|
||||
|
||||
transform.translation = translation;
|
||||
transform.rotation = rotation;
|
||||
}
|
||||
|
||||
if let Ok(mut transform) = transforms.get_mut(cameras.1) {
|
||||
let Pose {
|
||||
translation,
|
||||
rotation,
|
||||
} = right_view.pose();
|
||||
|
||||
transform.translation = translation;
|
||||
transform.rotation = rotation;
|
||||
}
|
||||
|
||||
manual_texture_views.insert(RIGHT_XR_TEXTURE_HANDLE, right);
|
||||
manual_texture_views.insert(LEFT_XR_TEXTURE_HANDLE, left);
|
||||
}
|
||||
|
||||
pub fn end_frame(session: NonSend<Session>) {
|
||||
|
||||
Reference in New Issue
Block a user