diff --git a/dash-frontend/assets/lang/de.json b/dash-frontend/assets/lang/de.json index d9bd06af..4a18a994 100644 --- a/dash-frontend/assets/lang/de.json +++ b/dash-frontend/assets/lang/de.json @@ -192,7 +192,9 @@ "APPLY_CHANGES_RESTART": "WayVR neu starten, um die Änderungen zu übernehmen?", "COLOR_PALETTE_HELP": "Klicken, um Farbpaletten zu durchsuchen.\nUm eigene zu erstellen, besuche die Webseite!", "COLOR_PALETTE_CUSTOM_URL": "Füge dein eigenes benutzerdefiniertes Thema hinzu!", - "COLOR_PALETTE_CUSTOM_URL2": "Klicken Sie hier, um das Web-Handbuch zu öffnen." + "COLOR_PALETTE_CUSTOM_URL2": "Klicken Sie hier, um das Web-Handbuch zu öffnen.", + "SNAP_ANGLE_DEG": "Snap-Winkel für Fensterneigung und Rollwinkel", + "SNAP_ANGLE_DEG_HELP": "Gegriffene Fenster rasten in diesem Winkel auf der Nick- und Rollachse ein." }, "HELLO": "Hallo!", "AUDIO": { diff --git a/dash-frontend/assets/lang/en.json b/dash-frontend/assets/lang/en.json index 6a176d6b..7bbf534a 100644 --- a/dash-frontend/assets/lang/en.json +++ b/dash-frontend/assets/lang/en.json @@ -174,6 +174,8 @@ "SCREEN_RENDER_DOWN_HELP": "Helps with aliasing on high-res screens", "SCROLL_SPEED": "Scroll speed", "SELECT_VARIANT": "Select variant", + "SNAP_ANGLE_DEG": "Window pitch & roll snap angle", + "SNAP_ANGLE_DEG_HELP": "Grabbed windows snap to this angle on pitch and roll axis.", "ENABLE_WATCH": "Enable watch", "SETS_ON_WATCH": "Sets on watch", "SHOW_WELCOME_SCREEN": "Show welcome screen", diff --git a/dash-frontend/assets/lang/es.json b/dash-frontend/assets/lang/es.json index cb4b0121..c49727e8 100644 --- a/dash-frontend/assets/lang/es.json +++ b/dash-frontend/assets/lang/es.json @@ -192,7 +192,9 @@ "APPLY_CHANGES_RESTART": "¿Reiniciar WayVR para aplicar los cambios?", "COLOR_PALETTE_HELP": "Haz clic para explorar paletas de colores.\nPara crear paletas personalizadas, ¡visita el sitio web!", "COLOR_PALETTE_CUSTOM_URL": "¡Añade tu propio tema personalizado!", - "COLOR_PALETTE_CUSTOM_URL2": "Haz clic para abrir el manual web." + "COLOR_PALETTE_CUSTOM_URL2": "Haz clic para abrir el manual web.", + "SNAP_ANGLE_DEG": "Ángulo de ajuste de cabeceo y alabeo de la ventana", + "SNAP_ANGLE_DEG_HELP": "Las ventanas agarradas se ajustan a este ángulo en los ejes de cabeceo y balanceo." }, "HELLO": "¡Hola!", "AUDIO": { diff --git a/dash-frontend/assets/lang/it.json b/dash-frontend/assets/lang/it.json index bd76c018..75c9103e 100644 --- a/dash-frontend/assets/lang/it.json +++ b/dash-frontend/assets/lang/it.json @@ -213,7 +213,9 @@ "APPLY_CHANGES_RESTART": "Riavviare WayVR per applicare le modifiche?", "COLOR_PALETTE_HELP": "Clicca per sfogliare le palette di colori.\nPer crearne di personalizzate, visita il sito web!", "COLOR_PALETTE_CUSTOM_URL": "Aggiungi il tuo tema personalizzato!", - "COLOR_PALETTE_CUSTOM_URL2": "Clicca per aprire il manuale web." + "COLOR_PALETTE_CUSTOM_URL2": "Clicca per aprire il manuale web.", + "SNAP_ANGLE_DEG": "Angolo di scatto beccheggio e rollio finestra", + "SNAP_ANGLE_DEG_HELP": "Le finestre afferrate si allineano a questo angolo sugli assi di beccheggio e rollio." }, "APPLICATION_LAUNCHER": "Lanciatore applicazioni", "APPLICATION_STARTED": "Applicazione avviata", diff --git a/dash-frontend/assets/lang/ja.json b/dash-frontend/assets/lang/ja.json index d9182e8d..c9cc7c06 100644 --- a/dash-frontend/assets/lang/ja.json +++ b/dash-frontend/assets/lang/ja.json @@ -192,7 +192,9 @@ "APPLY_CHANGES_RESTART": "変更を適用するためにWayVRを再起動しますか?", "COLOR_PALETTE_HELP": "クリックしてカラーパレットを選択してください。\nカスタムパレットの作成については、ウェブサイトを参照してください!", "COLOR_PALETTE_CUSTOM_URL": "カスタムテーマを追加しましょう!", - "COLOR_PALETTE_CUSTOM_URL2": "クリックしてウェブマニュアルを開く。" + "COLOR_PALETTE_CUSTOM_URL2": "クリックしてウェブマニュアルを開く。", + "SNAP_ANGLE_DEG": "ウィンドウのピッチ・ロールのスナップ角度", + "SNAP_ANGLE_DEG_HELP": "掴んでいるウィンドウが、ピッチ軸とロール軸でこの角度にスナップします。" }, "HELLO": "こんにちは!", "AUDIO": { diff --git a/dash-frontend/assets/lang/pl.json b/dash-frontend/assets/lang/pl.json index a0bab230..1a543be0 100644 --- a/dash-frontend/assets/lang/pl.json +++ b/dash-frontend/assets/lang/pl.json @@ -187,7 +187,9 @@ "APPLY_CHANGES_RESTART": "Zrestartować WayVR, aby zastosować zmiany?", "COLOR_PALETTE_HELP": "Kliknij, aby przeglądać palety kolorów.\nAby stworzyć własne, odwiedź stronę internetową!", "COLOR_PALETTE_CUSTOM_URL": "Dodaj własny motyw!", - "COLOR_PALETTE_CUSTOM_URL2": "Kliknij, aby otworzyć instrukcję internetową." + "COLOR_PALETTE_CUSTOM_URL2": "Kliknij, aby otworzyć instrukcję internetową.", + "SNAP_ANGLE_DEG": "Kąt przyciągania pochylenia i przechyłu okna", + "SNAP_ANGLE_DEG_HELP": "Chwycone okna przyciągają się do tego kąta na osi pochylenia i obrotu." }, "APPLICATION_LAUNCHER": "Uruchamiacz aplikacji", "APPLICATIONS": "Aplikacje", diff --git a/dash-frontend/assets/lang/zh_CN.json b/dash-frontend/assets/lang/zh_CN.json index 07ca4655..f39ae596 100644 --- a/dash-frontend/assets/lang/zh_CN.json +++ b/dash-frontend/assets/lang/zh_CN.json @@ -213,7 +213,9 @@ "APPLY_CHANGES_RESTART": "重启 WayVR 以应用更改吗?", "COLOR_PALETTE_HELP": "点击浏览配色方案。\n如需创建自定义配色,请参阅网站!", "COLOR_PALETTE_CUSTOM_URL": "添加自定义主题!", - "COLOR_PALETTE_CUSTOM_URL2": "点击打开网页版手册。" + "COLOR_PALETTE_CUSTOM_URL2": "点击打开网页版手册。", + "SNAP_ANGLE_DEG": "窗口俯仰与翻滚捕捉角度", + "SNAP_ANGLE_DEG_HELP": "抓取的窗口在俯仰轴和翻滚轴上将对齐到此角度。" }, "APPLICATION_LAUNCHER": "应用启动器", "APPLICATION_STARTED": "应用已启动", diff --git a/dash-frontend/src/tab/settings/mod.rs b/dash-frontend/src/tab/settings/mod.rs index 3082d1dc..36472a7b 100644 --- a/dash-frontend/src/tab/settings/mod.rs +++ b/dash-frontend/src/tab/settings/mod.rs @@ -320,6 +320,7 @@ pub(crate) enum SettingType { PointerLerpFactor, ScreenRenderDown, ScrollSpeed, + SnapAngleDeg, EnableWatch, SetsOnWatch, SpaceDragMultiplier, @@ -389,6 +390,7 @@ impl SettingType { Self::LongPressDuration => &mut config.long_press_duration, Self::PointerLerpFactor => &mut config.pointer_lerp_factor, Self::ScrollSpeed => &mut config.scroll_speed, + Self::SnapAngleDeg => &mut config.snap_angle_deg, Self::SpaceDragMultiplier => &mut config.space_drag_multiplier, Self::SpaceGravityDamping => &mut config.space_gravity_damping, Self::SpaceGravityFlingStrength => &mut config.space_gravity_fling_strength, @@ -500,6 +502,7 @@ impl SettingType { Self::ScrollSpeed => Ok("APP_SETTINGS.SCROLL_SPEED"), Self::EnableWatch => Ok("APP_SETTINGS.ENABLE_WATCH"), Self::SetsOnWatch => Ok("APP_SETTINGS.SETS_ON_WATCH"), + Self::SnapAngleDeg => Ok("APP_SETTINGS.SNAP_ANGLE_DEG"), Self::SpaceDragMultiplier => Ok("APP_SETTINGS.SPACE_DRAG_MULTIPLIER"), Self::SpaceDragUnlocked => Ok("APP_SETTINGS.SPACE_DRAG_UNLOCKED"), Self::SpaceGravityDamping => Ok("APP_SETTINGS.SPACE_GRAVITY_DAMPING"), @@ -536,6 +539,7 @@ impl SettingType { Self::KeyboardMiddleClick => Some("APP_SETTINGS.KEYBOARD_MIDDLE_CLICK_HELP"), Self::LeftHandedMouse => Some("APP_SETTINGS.LEFT_HANDED_MOUSE_HELP"), Self::ScreenRenderDown => Some("APP_SETTINGS.SCREEN_RENDER_DOWN_HELP"), + Self::SnapAngleDeg => Some("APP_SETTINGS.SNAP_ANGLE_DEG_HELP"), Self::SpaceGravityDamping => Some("APP_SETTINGS.SPACE_GRAVITY_DAMPING_HELP"), Self::SpaceGravityFlingStrength => Some("APP_SETTINGS.SPACE_GRAVITY_FLING_STRENGTH_HELP"), Self::SpaceGravityFloorHeight => Some("APP_SETTINGS.SPACE_GRAVITY_FLOOR_HEIGHT_HELP"), diff --git a/dash-frontend/src/tab/settings/tab_controls.rs b/dash-frontend/src/tab/settings/tab_controls.rs index faab6d11..44d2ef60 100644 --- a/dash-frontend/src/tab/settings/tab_controls.rs +++ b/dash-frontend/src/tab/settings/tab_controls.rs @@ -10,10 +10,10 @@ use crate::util::popup_manager::PopupHolder; use crate::{ frontend::FrontendTasks, tab::settings::{ - SettingType, SettingsMountParams, SettingsTab, macros::{options_category, options_checkbox, options_dropdown, options_slider_f32, options_slider_i32}, + SettingType, SettingsMountParams, SettingsTab, }, - views::{ViewUpdateParams, input_profiles}, + views::{input_profiles, ViewUpdateParams}, }; #[derive(Clone)] @@ -104,6 +104,7 @@ impl State { options_checkbox(par.mp, c, SettingType::InvertScrollDirectionY)?; options_slider_f32(par.mp, c, SettingType::ScrollSpeed, 0.1, 5.0, 0.1)?; options_slider_f32(par.mp, c, SettingType::LongPressDuration, 0.1, 2.0, 0.1)?; + options_slider_f32(par.mp, c, SettingType::SnapAngleDeg, 0., 45., 5.)?; if par.feats.openxr { options_slider_f32(par.mp, c, SettingType::PointerLerpFactor, 0.1, 1.0, 0.1)?; diff --git a/wayvr/src/backend/input.rs b/wayvr/src/backend/input.rs index da5dac93..f0633645 100644 --- a/wayvr/src/backend/input.rs +++ b/wayvr/src/backend/input.rs @@ -888,7 +888,12 @@ where app.anchor.translation = pointer.pose.transform_point3a(grab_data.offset.translation); let scale = scalar_scale(&app.anchor); - realign(&mut app.anchor, &app.input_state.hmd, scale); + realign( + &mut app.anchor, + &app.input_state.hmd, + scale, + app.session.config.snap_angle_deg, + ); } } else { // single grab resize @@ -912,7 +917,12 @@ where let scale = scalar_scale(&overlay_state.transform); overlay_state.transform.translation = pointer.pose.transform_point3a(grab_data.offset.translation); - realign(&mut overlay_state.transform, &app.input_state.hmd, scale); + realign( + &mut overlay_state.transform, + &app.input_state.hmd, + scale, + app.session.config.snap_angle_deg, + ); } overlay.config.pause_movement = true; overlay.config.dirty = true; diff --git a/wayvr/src/config.rs b/wayvr/src/config.rs index c83da477..b9c9386d 100644 --- a/wayvr/src/config.rs +++ b/wayvr/src/config.rs @@ -187,6 +187,7 @@ pub struct AutoSettings { pub whisper_model: Arc, pub default_overlay_scale: f32, pub color_palette: Arc, + pub snap_angle_deg: f32, } fn get_settings_path() -> PathBuf { @@ -252,6 +253,7 @@ pub fn save_settings(config: &GeneralConfig) -> anyhow::Result<()> { whisper_model: config.whisper_model.clone(), default_overlay_scale: config.default_overlay_scale, color_palette: config.color_palette.clone(), + snap_angle_deg: config.snap_angle_deg, }; let json = serde_json::to_string_pretty(&conf).unwrap(); // want panic diff --git a/wayvr/src/overlays/dashboard.rs b/wayvr/src/overlays/dashboard.rs index b8ce2316..d8518522 100644 --- a/wayvr/src/overlays/dashboard.rs +++ b/wayvr/src/overlays/dashboard.rs @@ -329,7 +329,7 @@ fn tutorial_spawn_effect(app: &mut AppState) -> anyhow::Result<()> { let hmd = &app.input_state.hmd; let pos = hmd.translation + hmd.z_axis * -2.0; let mut transform = Affine3A::from_rotation_translation(Quat::IDENTITY, pos.into()); - realign(&mut transform, hmd, 1.0); + realign(&mut transform, hmd, 1.0, app.session.config.snap_angle_deg); owc.active_state = Some(OverlayWindowState { saved_transform: Some(Affine3A::from_translation(vec3(0., -0.1, -0.9))), diff --git a/wayvr/src/windowing/window.rs b/wayvr/src/windowing/window.rs index 01db2fcb..5c9fdc73 100644 --- a/wayvr/src/windowing/window.rs +++ b/wayvr/src/windowing/window.rs @@ -1,4 +1,4 @@ -use glam::{Affine3A, Mat3A, Quat, Vec3, Vec3A}; +use glam::{Affine3A, EulerRot, Mat3A, Quat, Vec3, Vec3A}; use idmap_derive::IntegerId; use std::{f32::consts::PI, sync::Arc}; use wlx_common::windowing::{OverlayWindowState, Positioning}; @@ -192,7 +192,12 @@ impl OverlayWindowConfig { }; if align_to_hmd { - realign(&mut state.transform, &app.input_state.hmd, scale); + realign( + &mut state.transform, + &app.input_state.hmd, + scale, + app.session.config.snap_angle_deg, + ); } self.dirty = true; @@ -250,13 +255,18 @@ impl OverlayWindowConfig { if align_to_hmd || (state.grabbable && hard_reset) { let scale = scalar_scale(&cur_transform); - realign(&mut state.transform, &app.input_state.hmd, scale); + realign( + &mut state.transform, + &app.input_state.hmd, + scale, + app.session.config.snap_angle_deg, + ); } self.dirty = true; } } -pub fn realign(transform: &mut Affine3A, hmd: &Affine3A, scale: f32) { +pub fn realign(transform: &mut Affine3A, hmd: &Affine3A, scale: f32, snap_angle: f32) { let to_hmd = hmd.translation - transform.translation; let up_dir: Vec3A; @@ -289,8 +299,20 @@ pub fn realign(transform: &mut Affine3A, hmd: &Affine3A, scale: f32) { let col_y = col_z.cross(col_x).normalize(); let col_x = col_x.normalize(); - let rot = Mat3A::from_quat(Quat::from_axis_angle(Vec3::Y, PI)); - transform.matrix3 = Mat3A::from_cols(col_x, col_y, col_z).mul_scalar(scale) * rot; + let flip = Mat3A::from_quat(Quat::from_axis_angle(Vec3::Y, PI)); + let mut rotation = Mat3A::from_cols(col_x, col_y, col_z) * flip; + + if snap_angle > f32::EPSILON { + let snap_step = snap_angle.to_radians(); + let (yaw, pitch, roll) = rotation.to_euler(EulerRot::YXZ); + + let pitch = (pitch / snap_step).round() * snap_step; + let roll = (roll / snap_step).round() * snap_step; + + rotation = Mat3A::from_euler(EulerRot::YXZ, yaw, pitch, roll); + } + + transform.matrix3 = rotation.mul_scalar(scale); } pub fn scalar_scale(a: &Affine3A) -> f32 { diff --git a/wlx-common/src/config.rs b/wlx-common/src/config.rs index 88a93beb..289ba055 100644 --- a/wlx-common/src/config.rs +++ b/wlx-common/src/config.rs @@ -475,4 +475,7 @@ pub struct GeneralConfig { #[serde(default = "def_default")] pub color_palette: Arc, + + #[serde(default)] + pub snap_angle_deg: f32, }