Files
bevy_kneeboard/src/main.rs
2026-02-21 19:08:35 +01:00

93 lines
2.8 KiB
Rust

//! A simple 3D scene with light shining over a cube sitting on a plane.
mod kneeboardplugin;
mod vrcontrollerplugin;
mod vrplugin;
use bevy_cef::prelude::{Browsers, MeshAabb, WebviewSize, WebviewSource};
use bevy_mod_openxr::openxr_session_running;
use bevy_mod_xr::camera::XrCamera;
use vrplugin::VrPlugin;
use bevy::prelude::*;
use crate::{
kneeboardplugin::{Kneeboard, KneeboardPlugin, LookedAt},
vrcontrollerplugin::VrControllersPlugin,
};
fn main() {
App::new()
.add_plugins((VrPlugin, MeshPickingPlugin))
.add_plugins(VrControllersPlugin)
.add_plugins(KneeboardPlugin)
.insert_resource(ClearColor(Color::NONE))
.add_systems(Update, head_pointer.run_if(openxr_session_running))
.run();
}
#[allow(clippy::type_complexity)]
fn head_pointer(
browsers: NonSend<Browsers>,
webviews: Query<(Entity, &WebviewSize), With<WebviewSource>>,
headset: Query<&GlobalTransform, With<XrCamera>>,
kneeboard: Query<(&GlobalTransform, Option<&LookedAt>), With<Kneeboard>>,
aabb: MeshAabb,
) {
let Ok((webview, size)) = webviews.single() else {
return;
};
let tex_size = size.0;
if let Some(gt) = headset.into_iter().next() {
let Ok((plane_tf, looked_at)) = kneeboard.single() else {
return;
};
if looked_at.is_some() {
// this is inverted for some reason wtf
return;
}
let (min, max) = aabb.calculate_local(webview);
let plane_size = Vec2::new(max.x - min.x, max.y - min.y);
let ray = Ray3d::new(gt.translation(), gt.forward());
let n = plane_tf.forward().as_vec3();
let Some(t) = ray.intersect_plane(
plane_tf.translation(),
InfinitePlane3d::new(plane_tf.forward()),
) else {
return;
};
let hit_world = ray.origin + ray.direction * t;
let local_hit = plane_tf.affine().inverse().transform_point(hit_world);
let local_normal = plane_tf.affine().inverse().transform_vector3(n).normalize();
let abs_normal = local_normal.abs();
let (u_coord, v_coord) = if abs_normal.z > abs_normal.x && abs_normal.z > abs_normal.y {
(local_hit.x, local_hit.y)
} else if abs_normal.y > abs_normal.x {
(local_hit.x, local_hit.z)
} else {
(local_hit.y, local_hit.z)
};
let w = plane_size.x;
let h = plane_size.y;
let u = (u_coord + w * 0.5) / w;
let v = (v_coord + h * 0.5) / h;
if !(0.0..=1.0).contains(&u) || !(0.0..=1.0).contains(&v) {
// outside plane bounds
return;
}
let px = (1.0 - u) * tex_size.x;
let py = (1.0 - v) * tex_size.y;
let pos = Vec2::new(px, py);
browsers.send_mouse_move(&webview, &[], pos, false);
}
}