use glam::{Quat, Vec3A}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; use crate::{ backend::overlay::{ui_transform, OverlayData, OverlayState, RelativeTo}, config::{ def_half, def_left, def_point7, def_watch_pos, def_watch_rot, load_known_yaml, ConfigType, }, config_io, gui::{ modular::{modular_canvas, ModularData, ModularUiConfig}, Canvas, }, state::{AppState, LeftRight}, }; pub const WATCH_NAME: &str = "watch"; pub fn create_watch(state: &AppState) -> anyhow::Result> where O: Default, { let config = load_known_yaml::(ConfigType::Watch); let relative_to = RelativeTo::Hand(state.session.config.watch_hand as usize); Ok(OverlayData { state: OverlayState { name: WATCH_NAME.into(), want_visible: true, interactable: true, spawn_scale: config.width, spawn_point: Vec3A::from_slice(&state.session.config.watch_pos), spawn_rotation: Quat::from_slice(&state.session.config.watch_rot), interaction_transform: ui_transform(&config.size), relative_to, ..Default::default() }, backend: Box::new(create_watch_canvas(Some(config), state)?), ..Default::default() }) } pub fn create_watch_canvas( config: Option, state: &AppState, ) -> anyhow::Result> { let config = config.unwrap_or_else(|| load_known_yaml::(ConfigType::Watch)); modular_canvas(&config.size, &config.elements, state) } pub fn watch_fade(app: &mut AppState, watch: &mut OverlayData) where D: Default, { if watch.state.saved_scale.is_some_and(|s| s < f32::EPSILON) { watch.state.want_visible = false; return; } let to_hmd = (watch.state.transform.translation - app.input_state.hmd.translation).normalize(); let watch_normal = watch .state .transform .transform_vector3a(Vec3A::NEG_Z) .normalize(); let dot = to_hmd.dot(watch_normal); if dot < app.session.config.watch_view_angle_min { watch.state.want_visible = false; } else { watch.state.want_visible = true; watch.state.alpha = (dot - app.session.config.watch_view_angle_min) / (app.session.config.watch_view_angle_max - app.session.config.watch_view_angle_min); watch.state.alpha += 0.1; watch.state.alpha = watch.state.alpha.clamp(0., 1.); } } #[derive(Deserialize, Serialize)] pub struct WatchConf { #[serde(default = "def_watch_pos")] pub watch_pos: [f32; 3], #[serde(default = "def_watch_rot")] pub watch_rot: [f32; 4], #[serde(default = "def_left")] pub watch_hand: LeftRight, #[serde(default = "def_half")] pub watch_view_angle_min: f32, #[serde(default = "def_point7")] pub watch_view_angle_max: f32, } fn get_config_path() -> PathBuf { let mut path = config_io::get_conf_d_path(); path.push("watch_state.yaml"); path } pub fn save_watch(app: &mut AppState) -> anyhow::Result<()> { let conf = WatchConf { watch_pos: app.session.config.watch_pos, watch_rot: app.session.config.watch_rot, watch_hand: app.session.config.watch_hand, watch_view_angle_min: app.session.config.watch_view_angle_min, watch_view_angle_max: app.session.config.watch_view_angle_max, }; let yaml = serde_yaml::to_string(&conf)?; std::fs::write(get_config_path(), yaml)?; Ok(()) }