omg: curved screens
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::{BinaryHeap, VecDeque},
|
collections::{BinaryHeap, VecDeque},
|
||||||
|
f32::consts::PI,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
@@ -7,7 +8,7 @@ use std::{
|
|||||||
#[cfg(feature = "openxr")]
|
#[cfg(feature = "openxr")]
|
||||||
use openxr as xr;
|
use openxr as xr;
|
||||||
|
|
||||||
use glam::{Affine3A, Vec2, Vec3A};
|
use glam::{Affine3A, Vec2, Vec3A, Vec3Swizzles};
|
||||||
use idmap::IdMap;
|
use idmap::IdMap;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
@@ -252,23 +253,79 @@ impl TaskContainer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn raycast(
|
pub fn raycast_plane(
|
||||||
source: &Affine3A,
|
source: &Affine3A,
|
||||||
source_fwd: Vec3A,
|
source_fwd: Vec3A,
|
||||||
plane: &Affine3A,
|
plane: &Affine3A,
|
||||||
plane_norm: Vec3A,
|
plane_norm: Vec3A,
|
||||||
) -> Option<(Vec3A, f32)> {
|
) -> Option<(f32, Vec2)> {
|
||||||
let plane_normal = plane.transform_vector3a(plane_norm);
|
let plane_normal = plane.transform_vector3a(plane_norm);
|
||||||
let ray_dir = source.transform_vector3a(source_fwd);
|
let ray_dir = source.transform_vector3a(source_fwd);
|
||||||
|
|
||||||
let d = plane.translation.dot(-plane_normal);
|
let d = plane.translation.dot(-plane_normal);
|
||||||
let dist = -(d + source.translation.dot(plane_normal)) / ray_dir.dot(plane_normal);
|
let dist = -(d + source.translation.dot(plane_normal)) / ray_dir.dot(plane_normal);
|
||||||
|
|
||||||
if dist < 0.0 {
|
let hit_local = plane
|
||||||
// plane is behind the caster
|
.inverse()
|
||||||
|
.transform_point3a(source.translation + ray_dir * dist)
|
||||||
|
.xy();
|
||||||
|
|
||||||
|
Some((dist, hit_local))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn raycast_cylinder(
|
||||||
|
source: &Affine3A,
|
||||||
|
source_fwd: Vec3A,
|
||||||
|
plane: &Affine3A,
|
||||||
|
curvature: f32,
|
||||||
|
) -> Option<(f32, Vec2)> {
|
||||||
|
// this is solved locally; (0,0) is the center of the cylinder, and the cylinder is aligned with the Y axis
|
||||||
|
let size = plane.x_axis.length();
|
||||||
|
let to_local = Affine3A {
|
||||||
|
matrix3: plane.matrix3.mul_scalar(1.0 / size),
|
||||||
|
translation: plane.translation,
|
||||||
|
}
|
||||||
|
.inverse();
|
||||||
|
|
||||||
|
let r = size / (2.0 * PI * curvature);
|
||||||
|
|
||||||
|
let ray_dir = to_local.transform_vector3a(source.transform_vector3a(source_fwd));
|
||||||
|
let ray_origin = to_local.transform_point3a(source.translation) + Vec3A::NEG_Z * r;
|
||||||
|
|
||||||
|
let d = ray_dir.xz();
|
||||||
|
let s = ray_origin.xz();
|
||||||
|
|
||||||
|
let a = d.dot(d);
|
||||||
|
let b = d.dot(s);
|
||||||
|
let c = s.dot(s) - r * r;
|
||||||
|
|
||||||
|
let d = (b * b) - (a * c);
|
||||||
|
if d < f32::EPSILON {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let hit_pos = source.translation + ray_dir * dist;
|
let sqrt_d = d.sqrt();
|
||||||
Some((hit_pos, dist))
|
|
||||||
|
let t1 = (-b - sqrt_d) / a;
|
||||||
|
let t2 = (-b + sqrt_d) / a;
|
||||||
|
|
||||||
|
let t = t1.max(t2);
|
||||||
|
|
||||||
|
if t < f32::EPSILON {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut hit_local = ray_origin + ray_dir * t;
|
||||||
|
if hit_local.z > 0.0 {
|
||||||
|
// hitting the opposite half of the cylinder
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let max_angle = 2.0 * (size / (2.0 * r));
|
||||||
|
let x_angle = (hit_local.x / r).asin();
|
||||||
|
|
||||||
|
hit_local.x = x_angle / max_angle;
|
||||||
|
hit_local.y = hit_local.y / size;
|
||||||
|
|
||||||
|
Some((t, hit_local.xy()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use smallvec::{smallvec, SmallVec};
|
|||||||
use crate::state::AppState;
|
use crate::state::AppState;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
common::{raycast, OverlayContainer},
|
common::{raycast_cylinder, raycast_plane, OverlayContainer},
|
||||||
overlay::OverlayData,
|
overlay::OverlayData,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -234,7 +234,8 @@ impl InteractionHandler for DummyInteractionHandler {
|
|||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
struct RayHit {
|
struct RayHit {
|
||||||
overlay: usize,
|
overlay: usize,
|
||||||
hit_pos: Vec3A,
|
global_pos: Vec3A,
|
||||||
|
local_pos: Vec2,
|
||||||
dist: f32,
|
dist: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,7 +363,28 @@ where
|
|||||||
|
|
||||||
if pointer.now.scroll.abs() > 0.1 {
|
if pointer.now.scroll.abs() > 0.1 {
|
||||||
let scroll = pointer.now.scroll;
|
let scroll = pointer.now.scroll;
|
||||||
hovered.backend.on_scroll(app, &hit, scroll);
|
if app.input_state.pointers[1 - idx]
|
||||||
|
.interaction
|
||||||
|
.grabbed
|
||||||
|
.is_some_and(|x| x.grabbed_id == hit.overlay)
|
||||||
|
{
|
||||||
|
let is_portrait = hovered.view().is_some_and(|v| {
|
||||||
|
let extent = v.image().extent();
|
||||||
|
extent[0] >= extent[1]
|
||||||
|
});
|
||||||
|
|
||||||
|
if is_portrait {
|
||||||
|
let cur = hovered.state.curvature.unwrap_or(0.0);
|
||||||
|
let new = (cur - scroll * 0.01).min(0.35);
|
||||||
|
if new <= f32::EPSILON {
|
||||||
|
hovered.state.curvature = None;
|
||||||
|
} else {
|
||||||
|
hovered.state.curvature = Some(new);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hovered.backend.on_scroll(app, &hit, scroll);
|
||||||
|
}
|
||||||
pointer = &mut app.input_state.pointers[idx];
|
pointer = &mut app.input_state.pointers[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,7 +415,11 @@ impl Pointer {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(hit) = self.ray_test(overlay.state.id, &overlay.state.transform) {
|
if let Some(hit) = self.ray_test(
|
||||||
|
overlay.state.id,
|
||||||
|
&overlay.state.transform,
|
||||||
|
&overlay.state.curvature,
|
||||||
|
) {
|
||||||
if hit.dist.is_infinite() || hit.dist.is_nan() {
|
if hit.dist.is_infinite() || hit.dist.is_nan() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -408,12 +434,8 @@ impl Pointer {
|
|||||||
|
|
||||||
let uv = overlay
|
let uv = overlay
|
||||||
.state
|
.state
|
||||||
.transform
|
.interaction_transform
|
||||||
.inverse()
|
.transform_point2(hit.local_pos);
|
||||||
.transform_point3a(hit.hit_pos)
|
|
||||||
.truncate();
|
|
||||||
|
|
||||||
let uv = overlay.state.interaction_transform.transform_point2(uv);
|
|
||||||
|
|
||||||
if uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0 {
|
if uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0 {
|
||||||
continue;
|
continue;
|
||||||
@@ -489,14 +511,26 @@ impl Pointer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ray_test(&self, overlay: usize, plane: &Affine3A) -> Option<RayHit> {
|
fn ray_test(
|
||||||
let Some((hit_pos, dist)) = raycast(&self.pose, Vec3A::NEG_Z, plane, Vec3A::NEG_Z) else {
|
&self,
|
||||||
|
overlay: usize,
|
||||||
|
transform: &Affine3A,
|
||||||
|
curvature: &Option<f32>,
|
||||||
|
) -> Option<RayHit> {
|
||||||
|
let (dist, local_pos) = match curvature {
|
||||||
|
Some(curvature) => raycast_cylinder(&self.pose, Vec3A::NEG_Z, transform, *curvature),
|
||||||
|
_ => raycast_plane(&self.pose, Vec3A::NEG_Z, transform, Vec3A::NEG_Z),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
if dist < 0.0 {
|
||||||
|
// hit is behind us
|
||||||
return None;
|
return None;
|
||||||
};
|
}
|
||||||
|
|
||||||
Some(RayHit {
|
Some(RayHit {
|
||||||
overlay,
|
overlay,
|
||||||
hit_pos,
|
global_pos: self.pose.transform_point3a(Vec3A::NEG_Z * dist),
|
||||||
|
local_pos,
|
||||||
dist,
|
dist,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use core::f32;
|
||||||
|
|
||||||
use glam::Vec4;
|
use glam::Vec4;
|
||||||
use ovr_overlay::{
|
use ovr_overlay::{
|
||||||
overlay::{OverlayHandle, OverlayManager},
|
overlay::{OverlayHandle, OverlayManager},
|
||||||
@@ -21,7 +23,6 @@ pub(super) struct OpenVrOverlayData {
|
|||||||
pub(super) last_image: Option<u64>,
|
pub(super) last_image: Option<u64>,
|
||||||
pub(super) visible: bool,
|
pub(super) visible: bool,
|
||||||
pub(super) color: Vec4,
|
pub(super) color: Vec4,
|
||||||
pub(super) curvature: f32,
|
|
||||||
pub(super) sort_order: u32,
|
pub(super) sort_order: u32,
|
||||||
pub(crate) width: f32,
|
pub(crate) width: f32,
|
||||||
pub(super) override_width: bool,
|
pub(super) override_width: bool,
|
||||||
@@ -88,6 +89,8 @@ impl OverlayData<OpenVrOverlayData> {
|
|||||||
) {
|
) {
|
||||||
if self.data.visible {
|
if self.data.visible {
|
||||||
if self.state.dirty {
|
if self.state.dirty {
|
||||||
|
self.upload_curvature(overlay);
|
||||||
|
|
||||||
self.upload_transform(universe, overlay);
|
self.upload_transform(universe, overlay);
|
||||||
self.upload_alpha(overlay);
|
self.upload_alpha(overlay);
|
||||||
self.state.dirty = false;
|
self.state.dirty = false;
|
||||||
@@ -172,7 +175,7 @@ impl OverlayData<OpenVrOverlayData> {
|
|||||||
log::debug!("{}: No overlay handle", self.state.name);
|
log::debug!("{}: No overlay handle", self.state.name);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if let Err(e) = overlay.set_curvature(handle, self.data.curvature) {
|
if let Err(e) = overlay.set_curvature(handle, self.state.curvature.unwrap_or(0.0)) {
|
||||||
log::error!(
|
log::error!(
|
||||||
"{}: Failed to set overlay curvature: {}",
|
"{}: Failed to set overlay curvature: {}",
|
||||||
self.state.name,
|
self.state.name,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use anyhow::{bail, ensure};
|
use anyhow::{bail, ensure};
|
||||||
use glam::{Affine3A, Quat, Vec3};
|
use glam::{Affine3A, Quat, Vec3, Vec3A};
|
||||||
use openxr as xr;
|
use openxr as xr;
|
||||||
use xr::OverlaySessionCreateFlagsEXTX;
|
use xr::OverlaySessionCreateFlagsEXTX;
|
||||||
|
|
||||||
@@ -139,12 +139,14 @@ fn quat_lerp(a: Quat, mut b: Quat, t: f32) -> Quat {
|
|||||||
.normalize()
|
.normalize()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn transform_to_posef(transform: &Affine3A) -> xr::Posef {
|
pub(super) fn transform_to_norm_quat(transform: &Affine3A) -> Quat {
|
||||||
let translation = transform.translation;
|
|
||||||
let norm_mat3 = transform
|
let norm_mat3 = transform
|
||||||
.matrix3
|
.matrix3
|
||||||
.mul_scalar(1.0 / transform.matrix3.x_axis.length());
|
.mul_scalar(1.0 / transform.matrix3.x_axis.length());
|
||||||
let mut rotation = Quat::from_mat3a(&norm_mat3).normalize();
|
Quat::from_mat3a(&norm_mat3).normalize()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn translation_rotation_to_posef(translation: Vec3A, mut rotation: Quat) -> xr::Posef {
|
||||||
if !rotation.is_finite() {
|
if !rotation.is_finite() {
|
||||||
rotation = Quat::IDENTITY;
|
rotation = Quat::IDENTITY;
|
||||||
}
|
}
|
||||||
@@ -163,3 +165,9 @@ pub(super) fn transform_to_posef(transform: &Affine3A) -> xr::Posef {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn transform_to_posef(transform: &Affine3A) -> xr::Posef {
|
||||||
|
let translation = transform.translation;
|
||||||
|
let rotation = transform_to_norm_quat(transform);
|
||||||
|
translation_rotation_to_posef(translation, rotation)
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use crate::{
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
swapchain::{create_swapchain_render_data, SwapchainRenderData},
|
swapchain::{create_swapchain_render_data, SwapchainRenderData},
|
||||||
XrState,
|
CompositionLayer, XrState,
|
||||||
};
|
};
|
||||||
|
|
||||||
static AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(1);
|
static AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(1);
|
||||||
@@ -135,7 +135,7 @@ impl LinePool {
|
|||||||
&'a mut self,
|
&'a mut self,
|
||||||
xr: &'a XrState,
|
xr: &'a XrState,
|
||||||
command_buffer: &mut WlxCommandBuffer,
|
command_buffer: &mut WlxCommandBuffer,
|
||||||
) -> anyhow::Result<Vec<xr::CompositionLayerQuad<xr::Vulkan>>> {
|
) -> anyhow::Result<Vec<CompositionLayer>> {
|
||||||
let mut quads = Vec::new();
|
let mut quads = Vec::new();
|
||||||
|
|
||||||
for line in self.lines.values_mut() {
|
for line in self.lines.values_mut() {
|
||||||
@@ -155,7 +155,7 @@ impl LinePool {
|
|||||||
height: inner.length,
|
height: inner.length,
|
||||||
});
|
});
|
||||||
|
|
||||||
quads.push(quad);
|
quads.push(CompositionLayer::Quad(quad));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -275,13 +275,18 @@ pub fn openxr_run(running: Arc<AtomicBool>) -> Result<(), BackendError> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(quad) = o.present_xr(&xr_state, &mut command_buffer)? {
|
let maybe_layer = o.present_xr(&xr_state, &mut command_buffer)?;
|
||||||
layers.push((dist_sq, quad));
|
if let CompositionLayer::None = maybe_layer {
|
||||||
};
|
continue;
|
||||||
|
}
|
||||||
|
layers.push((dist_sq, maybe_layer));
|
||||||
}
|
}
|
||||||
|
|
||||||
for quad in lines.present_xr(&xr_state, &mut command_buffer)? {
|
for maybe_layer in lines.present_xr(&xr_state, &mut command_buffer)? {
|
||||||
layers.push((0.0, quad));
|
if let CompositionLayer::None = maybe_layer {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
layers.push((0.0, maybe_layer));
|
||||||
}
|
}
|
||||||
|
|
||||||
command_buffer.build_and_execute_now()?;
|
command_buffer.build_and_execute_now()?;
|
||||||
@@ -290,7 +295,11 @@ pub fn openxr_run(running: Arc<AtomicBool>) -> Result<(), BackendError> {
|
|||||||
|
|
||||||
let frame_ref = layers
|
let frame_ref = layers
|
||||||
.iter()
|
.iter()
|
||||||
.map(|f| &f.1 as &xr::CompositionLayerBase<xr::Vulkan>)
|
.map(|f| match f.1 {
|
||||||
|
CompositionLayer::Quad(ref l) => l as &xr::CompositionLayerBase<xr::Vulkan>,
|
||||||
|
CompositionLayer::Cylinder(ref l) => l as &xr::CompositionLayerBase<xr::Vulkan>,
|
||||||
|
CompositionLayer::None => unreachable!(),
|
||||||
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
frame_stream.end(
|
frame_stream.end(
|
||||||
@@ -351,3 +360,9 @@ pub fn openxr_run(running: Arc<AtomicBool>) -> Result<(), BackendError> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) enum CompositionLayer<'a> {
|
||||||
|
None,
|
||||||
|
Quad(xr::CompositionLayerQuad<'a, xr::Vulkan>),
|
||||||
|
Cylinder(xr::CompositionLayerCylinderKHR<'a, xr::Vulkan>),
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
use glam::Vec3A;
|
||||||
use openxr as xr;
|
use openxr as xr;
|
||||||
use std::sync::Arc;
|
use std::{f32::consts::PI, sync::Arc};
|
||||||
use xr::{CompositionLayerFlags, EyeVisibility};
|
use xr::{CompositionLayerFlags, EyeVisibility};
|
||||||
|
|
||||||
use super::{helpers, swapchain::SwapchainRenderData, XrState};
|
use super::{helpers, swapchain::SwapchainRenderData, CompositionLayer, XrState};
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::{openxr::swapchain::create_swapchain_render_data, overlay::OverlayData},
|
backend::{openxr::swapchain::create_swapchain_render_data, overlay::OverlayData},
|
||||||
graphics::WlxCommandBuffer,
|
graphics::WlxCommandBuffer,
|
||||||
@@ -23,7 +24,7 @@ impl OverlayData<OpenXrOverlayData> {
|
|||||||
&'a mut self,
|
&'a mut self,
|
||||||
xr: &'a XrState,
|
xr: &'a XrState,
|
||||||
command_buffer: &mut WlxCommandBuffer,
|
command_buffer: &mut WlxCommandBuffer,
|
||||||
) -> anyhow::Result<Option<xr::CompositionLayerQuad<xr::Vulkan>>> {
|
) -> anyhow::Result<CompositionLayer> {
|
||||||
if let Some(new_view) = self.view() {
|
if let Some(new_view) = self.view() {
|
||||||
self.data.last_view = Some(new_view);
|
self.data.last_view = Some(new_view);
|
||||||
}
|
}
|
||||||
@@ -32,7 +33,7 @@ impl OverlayData<OpenXrOverlayData> {
|
|||||||
view.clone()
|
view.clone()
|
||||||
} else {
|
} else {
|
||||||
log::warn!("{}: Will not show - image not ready", self.state.name);
|
log::warn!("{}: Will not show - image not ready", self.state.name);
|
||||||
return Ok(None);
|
return Ok(CompositionLayer::None);
|
||||||
};
|
};
|
||||||
let extent = my_view.image().extent();
|
let extent = my_view.image().extent();
|
||||||
|
|
||||||
@@ -55,10 +56,8 @@ impl OverlayData<OpenXrOverlayData> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let sub_image = data.acquire_present_release(command_buffer, my_view, self.state.alpha)?;
|
let sub_image = data.acquire_present_release(command_buffer, my_view, self.state.alpha)?;
|
||||||
let posef = helpers::transform_to_posef(&self.state.transform);
|
|
||||||
|
|
||||||
let aspect_ratio = extent[1] as f32 / extent[0] as f32;
|
let aspect_ratio = extent[1] as f32 / extent[0] as f32;
|
||||||
|
|
||||||
let (scale_x, scale_y) = if aspect_ratio < 1.0 {
|
let (scale_x, scale_y) = if aspect_ratio < 1.0 {
|
||||||
let major = self.state.transform.matrix3.col(0).length();
|
let major = self.state.transform.matrix3.col(0).length();
|
||||||
(major, major * aspect_ratio)
|
(major, major * aspect_ratio)
|
||||||
@@ -67,17 +66,38 @@ impl OverlayData<OpenXrOverlayData> {
|
|||||||
(major / aspect_ratio, major)
|
(major / aspect_ratio, major)
|
||||||
};
|
};
|
||||||
|
|
||||||
let quad = xr::CompositionLayerQuad::new()
|
if let Some(curvature) = self.state.curvature {
|
||||||
.pose(posef)
|
let radius = scale_x / (2.0 * PI * curvature);
|
||||||
.sub_image(sub_image)
|
let quat = helpers::transform_to_norm_quat(&self.state.transform);
|
||||||
.eye_visibility(EyeVisibility::BOTH)
|
let center_point = self.state.transform.translation + quat.mul_vec3a(Vec3A::Z * radius);
|
||||||
.layer_flags(CompositionLayerFlags::CORRECT_CHROMATIC_ABERRATION)
|
|
||||||
.space(&xr.stage)
|
let posef = helpers::translation_rotation_to_posef(center_point, quat);
|
||||||
.size(xr::Extent2Df {
|
let angle = 2.0 * (scale_x / (2.0 * radius));
|
||||||
width: scale_x,
|
|
||||||
height: scale_y,
|
let cylinder = xr::CompositionLayerCylinderKHR::new()
|
||||||
});
|
.pose(posef)
|
||||||
Ok(Some(quad))
|
.sub_image(sub_image)
|
||||||
|
.eye_visibility(EyeVisibility::BOTH)
|
||||||
|
.layer_flags(CompositionLayerFlags::CORRECT_CHROMATIC_ABERRATION)
|
||||||
|
.space(&xr.stage)
|
||||||
|
.radius(radius)
|
||||||
|
.central_angle(angle)
|
||||||
|
.aspect_ratio(aspect_ratio);
|
||||||
|
Ok(CompositionLayer::Cylinder(cylinder))
|
||||||
|
} else {
|
||||||
|
let posef = helpers::transform_to_posef(&self.state.transform);
|
||||||
|
let quad = xr::CompositionLayerQuad::new()
|
||||||
|
.pose(posef)
|
||||||
|
.sub_image(sub_image)
|
||||||
|
.eye_visibility(EyeVisibility::BOTH)
|
||||||
|
.layer_flags(CompositionLayerFlags::CORRECT_CHROMATIC_ABERRATION)
|
||||||
|
.space(&xr.stage)
|
||||||
|
.size(xr::Extent2Df {
|
||||||
|
width: scale_x,
|
||||||
|
height: scale_y,
|
||||||
|
});
|
||||||
|
Ok(CompositionLayer::Quad(quad))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn after_input(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
pub(super) fn after_input(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ pub struct OverlayState {
|
|||||||
pub spawn_point: Vec3A,
|
pub spawn_point: Vec3A,
|
||||||
pub spawn_rotation: Quat,
|
pub spawn_rotation: Quat,
|
||||||
pub relative_to: RelativeTo,
|
pub relative_to: RelativeTo,
|
||||||
|
pub curvature: Option<f32>,
|
||||||
pub primary_pointer: Option<usize>,
|
pub primary_pointer: Option<usize>,
|
||||||
pub interaction_transform: Affine2,
|
pub interaction_transform: Affine2,
|
||||||
pub birthframe: usize,
|
pub birthframe: usize,
|
||||||
@@ -53,6 +54,7 @@ impl Default for OverlayState {
|
|||||||
dirty: true,
|
dirty: true,
|
||||||
alpha: 1.0,
|
alpha: 1.0,
|
||||||
relative_to: RelativeTo::None,
|
relative_to: RelativeTo::None,
|
||||||
|
curvature: None,
|
||||||
saved_point: None,
|
saved_point: None,
|
||||||
saved_scale: None,
|
saved_scale: None,
|
||||||
spawn_scale: 1.0,
|
spawn_scale: 1.0,
|
||||||
|
|||||||
Reference in New Issue
Block a user