Merge 20bd0b6435 into 013690580c
This commit is contained in:
commit
182802d115
|
|
@ -40,6 +40,32 @@ const POSE_ORIENTATION = [dataViewEnd(EPOCH_MS), FLOAT_SIZE, 16];
|
||||||
const IMU_PARITY_BYTE = [dataViewEnd(POSE_ORIENTATION), UINT8_SIZE, 1];
|
const IMU_PARITY_BYTE = [dataViewEnd(POSE_ORIENTATION), UINT8_SIZE, 1];
|
||||||
const DATA_VIEW_LENGTH = dataViewEnd(IMU_PARITY_BYTE);
|
const DATA_VIEW_LENGTH = dataViewEnd(IMU_PARITY_BYTE);
|
||||||
|
|
||||||
|
// Flatten a packed pose-orientation buffer (mat4 as 16 floats: orientation
|
||||||
|
// quaternions at floats [0..3] and [4..7], timestamps at [12..15]) to yaw-only.
|
||||||
|
// Uses a swing-twist decomposition about the NWU up-axis (Z): keep the Z and W
|
||||||
|
// components, zero X and Y, then renormalize. This removes all pitch and roll
|
||||||
|
// from the rendered orientation, so the display tracks only horizontal (yaw)
|
||||||
|
// head movement. The identity quaternion [0,0,0,1] is preserved.
|
||||||
|
function applyHorizonLock(poseOrientation) {
|
||||||
|
for (const offset of [0, 4]) {
|
||||||
|
const z = poseOrientation[offset + 2];
|
||||||
|
const w = poseOrientation[offset + 3];
|
||||||
|
const norm = Math.hypot(z, w);
|
||||||
|
poseOrientation[offset] = 0;
|
||||||
|
poseOrientation[offset + 1] = 0;
|
||||||
|
if (norm < 1e-6) {
|
||||||
|
// Yaw is undefined here (a pure 180-degree pitch/roll pose); fall
|
||||||
|
// back to identity rather than emitting a zero (non-unit) quaternion.
|
||||||
|
poseOrientation[offset + 2] = 0;
|
||||||
|
poseOrientation[offset + 3] = 1;
|
||||||
|
} else {
|
||||||
|
poseOrientation[offset + 2] = z / norm;
|
||||||
|
poseOrientation[offset + 3] = w / norm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return poseOrientation;
|
||||||
|
}
|
||||||
|
|
||||||
function checkParityByte(dataView) {
|
function checkParityByte(dataView) {
|
||||||
const parityByte = dataViewUint8(dataView, IMU_PARITY_BYTE);
|
const parityByte = dataViewUint8(dataView, IMU_PARITY_BYTE);
|
||||||
let parity = 0;
|
let parity = 0;
|
||||||
|
|
@ -132,6 +158,13 @@ export const DeviceDataStream = GObject.registerClass({
|
||||||
'Debug mode that allows for testing with moving IMU values without a device connected',
|
'Debug mode that allows for testing with moving IMU values without a device connected',
|
||||||
GObject.ParamFlags.READWRITE,
|
GObject.ParamFlags.READWRITE,
|
||||||
false
|
false
|
||||||
|
),
|
||||||
|
'horizon-lock': GObject.ParamSpec.boolean(
|
||||||
|
'horizon-lock',
|
||||||
|
'Horizon lock',
|
||||||
|
'Flatten the head pose to yaw-only (ignore pitch and roll) so the display only tracks horizontal head movement',
|
||||||
|
GObject.ParamFlags.READWRITE,
|
||||||
|
false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}, class DeviceDataStream extends GObject.Object {
|
}, class DeviceDataStream extends GObject.Object {
|
||||||
|
|
@ -212,9 +245,13 @@ export const DeviceDataStream = GObject.registerClass({
|
||||||
const version = dataViewUint8(dataView, VERSION);
|
const version = dataViewUint8(dataView, VERSION);
|
||||||
const enabled = dataViewUint8(dataView, ENABLED) !== 0 && version === DATA_LAYOUT_VERSION && validData;
|
const enabled = dataViewUint8(dataView, ENABLED) !== 0 && version === DATA_LAYOUT_VERSION && validData;
|
||||||
let poseOrientation = dataViewFloatArray(dataView, POSE_ORIENTATION);
|
let poseOrientation = dataViewFloatArray(dataView, POSE_ORIENTATION);
|
||||||
|
if (this.horizon_lock) applyHorizonLock(poseOrientation);
|
||||||
let posePosition = dataViewFloatArray(dataView, POSE_POSITION);
|
let posePosition = dataViewFloatArray(dataView, POSE_POSITION);
|
||||||
let smoothFollowEnabled = !this.legacy_follow_mode && dataViewUint8(dataView, SMOOTH_FOLLOW_ENABLED) !== 0;
|
let smoothFollowEnabled = !this.legacy_follow_mode && dataViewUint8(dataView, SMOOTH_FOLLOW_ENABLED) !== 0;
|
||||||
let smoothFollowOrigin = dataViewFloatArray(dataView, SMOOTH_FOLLOW_ORIGIN_DATA);
|
let smoothFollowOrigin = dataViewFloatArray(dataView, SMOOTH_FOLLOW_ORIGIN_DATA);
|
||||||
|
// The shader renders from the smooth-follow origin when follow is
|
||||||
|
// active, so horizon lock must flatten it too (same packed layout).
|
||||||
|
if (this.horizon_lock) applyHorizonLock(smoothFollowOrigin);
|
||||||
const imuResetState = enabled && validData && poseOrientation[0] === 0.0 && poseOrientation[1] === 0.0 && poseOrientation[2] === 0.0 && poseOrientation[3] === 1.0;
|
const imuResetState = enabled && validData && poseOrientation[0] === 0.0 && poseOrientation[1] === 0.0 && poseOrientation[2] === 0.0 && poseOrientation[3] === 1.0;
|
||||||
const customBannerEnabled = dataViewUint8(dataView, CUSTOM_BANNER_ENABLED) !== 0;
|
const customBannerEnabled = dataViewUint8(dataView, CUSTOM_BANNER_ENABLED) !== 0;
|
||||||
const sbsEnabled = dataViewUint8(dataView, SBS_ENABLED) !== 0;
|
const sbsEnabled = dataViewUint8(dataView, SBS_ENABLED) !== 0;
|
||||||
|
|
@ -281,6 +318,7 @@ export const DeviceDataStream = GObject.registerClass({
|
||||||
dataView = new DataView(buffer);
|
dataView = new DataView(buffer);
|
||||||
imuDateMs = dataViewBigUint(dataView, EPOCH_MS);
|
imuDateMs = dataViewBigUint(dataView, EPOCH_MS);
|
||||||
poseOrientation = dataViewFloatArray(dataView, POSE_ORIENTATION);
|
poseOrientation = dataViewFloatArray(dataView, POSE_ORIENTATION);
|
||||||
|
if (this.horizon_lock) applyHorizonLock(poseOrientation);
|
||||||
posePosition = dataViewFloatArray(dataView, POSE_POSITION);
|
posePosition = dataViewFloatArray(dataView, POSE_POSITION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ export default class BreezyDesktopExtension extends Extension {
|
||||||
this.settings.bind('disable-physical-displays', this._monitor_manager, 'disable-physical-displays', Gio.SettingsBindFlags.DEFAULT);
|
this.settings.bind('disable-physical-displays', this._monitor_manager, 'disable-physical-displays', Gio.SettingsBindFlags.DEFAULT);
|
||||||
this.settings.bind('legacy-follow-mode', Globals.data_stream, 'legacy-follow-mode', Gio.SettingsBindFlags.DEFAULT);
|
this.settings.bind('legacy-follow-mode', Globals.data_stream, 'legacy-follow-mode', Gio.SettingsBindFlags.DEFAULT);
|
||||||
this.settings.bind('debug-no-device', Globals.data_stream, 'debug-no-device', Gio.SettingsBindFlags.DEFAULT);
|
this.settings.bind('debug-no-device', Globals.data_stream, 'debug-no-device', Gio.SettingsBindFlags.DEFAULT);
|
||||||
|
this.settings.bind('horizon-lock', Globals.data_stream, 'horizon-lock', Gio.SettingsBindFlags.DEFAULT);
|
||||||
|
|
||||||
this._breezy_desktop_running_connection = Globals.data_stream.connect('notify::breezy-desktop-running',
|
this._breezy_desktop_running_connection = Globals.data_stream.connect('notify::breezy-desktop-running',
|
||||||
this._handle_breezy_desktop_running_change.bind(this));
|
this._handle_breezy_desktop_running_change.bind(this));
|
||||||
|
|
@ -732,6 +733,7 @@ export default class BreezyDesktopExtension extends Extension {
|
||||||
Gio.Settings.unbind(this.settings, 'headset-as-primary');
|
Gio.Settings.unbind(this.settings, 'headset-as-primary');
|
||||||
Gio.Settings.unbind(this.settings, 'disable-physical-displays');
|
Gio.Settings.unbind(this.settings, 'disable-physical-displays');
|
||||||
Gio.Settings.unbind(this.settings, 'debug-no-device');
|
Gio.Settings.unbind(this.settings, 'debug-no-device');
|
||||||
|
Gio.Settings.unbind(this.settings, 'horizon-lock');
|
||||||
|
|
||||||
if (this._monitor_manager) {
|
if (this._monitor_manager) {
|
||||||
this._monitor_manager.disable();
|
this._monitor_manager.disable();
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,15 @@
|
||||||
Enable curved display mode
|
Enable curved display mode
|
||||||
</description>
|
</description>
|
||||||
</key>
|
</key>
|
||||||
|
<key name="horizon-lock" type="b">
|
||||||
|
<default>
|
||||||
|
false
|
||||||
|
</default>
|
||||||
|
<summary>Horizon lock</summary>
|
||||||
|
<description>
|
||||||
|
Lock the display to the horizon, tracking only horizontal (yaw) head movement and ignoring pitch (vertical) and roll (tilt)
|
||||||
|
</description>
|
||||||
|
</key>
|
||||||
<key name="look-ahead-override" type="i">
|
<key name="look-ahead-override" type="i">
|
||||||
<default>
|
<default>
|
||||||
-1
|
-1
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ class ConnectedDevice(Gtk.Box):
|
||||||
follow_threshold_adjustment = Gtk.Template.Child()
|
follow_threshold_adjustment = Gtk.Template.Child()
|
||||||
follow_mode_switch = Gtk.Template.Child()
|
follow_mode_switch = Gtk.Template.Child()
|
||||||
curved_display_switch = Gtk.Template.Child()
|
curved_display_switch = Gtk.Template.Child()
|
||||||
|
horizon_lock_switch = Gtk.Template.Child()
|
||||||
top_features_group = Gtk.Template.Child()
|
top_features_group = Gtk.Template.Child()
|
||||||
virtual_displays_row = Gtk.Template.Child()
|
virtual_displays_row = Gtk.Template.Child()
|
||||||
add_virtual_display_menu = Gtk.Template.Child()
|
add_virtual_display_menu = Gtk.Template.Child()
|
||||||
|
|
@ -99,6 +100,7 @@ class ConnectedDevice(Gtk.Box):
|
||||||
self.follow_mode_switch,
|
self.follow_mode_switch,
|
||||||
self.follow_threshold_scale,
|
self.follow_threshold_scale,
|
||||||
self.curved_display_switch,
|
self.curved_display_switch,
|
||||||
|
self.horizon_lock_switch,
|
||||||
self.add_virtual_display_menu,
|
self.add_virtual_display_menu,
|
||||||
self.add_virtual_display_button,
|
self.add_virtual_display_button,
|
||||||
self.change_all_displays_distance_button,
|
self.change_all_displays_distance_button,
|
||||||
|
|
@ -124,6 +126,7 @@ class ConnectedDevice(Gtk.Box):
|
||||||
self.settings.bind('follow-threshold', self.follow_threshold_adjustment, 'value', Gio.SettingsBindFlags.DEFAULT)
|
self.settings.bind('follow-threshold', self.follow_threshold_adjustment, 'value', Gio.SettingsBindFlags.DEFAULT)
|
||||||
# self.settings.bind('widescreen-mode', self.widescreen_mode_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
|
# self.settings.bind('widescreen-mode', self.widescreen_mode_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
|
||||||
self.settings.bind('curved-display', self.curved_display_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
|
self.settings.bind('curved-display', self.curved_display_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
|
||||||
|
self.settings.bind('horizon-lock', self.horizon_lock_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
|
||||||
self.settings.bind('headset-display-as-viewport-center', self.headset_display_as_viewport_center_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
|
self.settings.bind('headset-display-as-viewport-center', self.headset_display_as_viewport_center_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
|
||||||
self.settings.bind('headset-as-primary', self.headset_as_primary_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
|
self.settings.bind('headset-as-primary', self.headset_as_primary_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
|
||||||
self.settings.bind('remove-virtual-displays-on-disable', self.remove_virtual_displays_on_disable_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
|
self.settings.bind('remove-virtual-displays-on-disable', self.remove_virtual_displays_on_disable_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,18 @@
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="AdwActionRow">
|
||||||
|
<property name="title" translatable="yes"><!-- feature switch -->Horizon lock</property>
|
||||||
|
<property name="subtitle" translatable="yes">Track only horizontal head movement; ignore looking up/down and head tilt.</property>
|
||||||
|
<property name="valign">2</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSwitch" id="horizon_lock_switch">
|
||||||
|
<property name="valign">3</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow">
|
<object class="AdwActionRow">
|
||||||
<property name="title" translatable="yes"><!-- feature switch -->Disable physical displays</property>
|
<property name="title" translatable="yes"><!-- feature switch -->Disable physical displays</property>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue