Files
bevy_oxr/+
Schmarni a0ac82b21c borked
2024-01-23 05:46:01 +01:00

295 lines
9.6 KiB
Plaintext

use crate::xr_input::{QuatConv, Vec3Conv};
use crate::{LEFT_XR_TEXTURE_HANDLE, RIGHT_XR_TEXTURE_HANDLE};
use bevy::core_pipeline::tonemapping::{DebandDither, Tonemapping};
use bevy::ecs::system::lifetimeless::Read;
use bevy::math::Vec3A;
use bevy::prelude::*;
use bevy::render::camera::{CameraProjection, CameraRenderGraph, RenderTarget};
use bevy::render::extract_component::ExtractComponent;
use bevy::render::primitives::Frustum;
use bevy::render::view::{ColorGrading, VisibleEntities};
use openxr::Fovf;
#[derive(Bundle)]
pub struct XrCamerasBundle {
pub left: XrCameraBundle,
pub right: XrCameraBundle,
}
impl XrCamerasBundle {
pub fn new() -> Self {
Self::default()
}
}
impl Default for XrCamerasBundle {
fn default() -> Self {
Self {
left: XrCameraBundle::new(Eye::Left),
right: XrCameraBundle::new(Eye::Right),
}
}
}
#[derive(Bundle)]
pub struct XrCameraBundle {
pub camera: Camera,
pub camera_render_graph: CameraRenderGraph,
pub xr_projection: XRProjection,
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(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> {
info!("extracting Transform");
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> {
info!("extracting Cam Type");
Some(*item)
}
}
#[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, ExtractComponent)]
#[reflect(Component, Default)]
pub struct XRProjection {
pub near: f32,
pub far: f32,
#[reflect(ignore)]
pub fov: Fovf,
}
impl Default for XRProjection {
fn default() -> Self {
Self {
near: 0.1,
far: 1000.,
fov: Default::default(),
}
}
}
impl XRProjection {
pub fn new(near: f32, far: f32, fov: Fovf) -> Self {
XRProjection { near, far, fov }
}
}
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
]
}
}
pub fn xr_camera_head_sync(
views: ResMut<crate::resources::XrViews>,
mut query: Query<(&mut Transform, &XrCameraType, &mut XRProjection)>,
) {
//TODO calculate HMD position
for (mut transform, camera_type, mut xr_projection) in query.iter_mut() {
let view_idx = match camera_type {
XrCameraType::Xr(eye) => *eye as usize,
// 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,
};
xr_projection.fov = view.fov;
transform.rotation = view.pose.orientation.to_quat();
transform.translation = view.pose.position.to_vec3();
}
}