Add support for testing without glasses connected, fix zoom so it always comes towards the camera
This commit is contained in:
parent
ed6981567c
commit
efdb6b464c
|
|
@ -51,6 +51,28 @@ function checkParityByte(dataView) {
|
|||
return parityByte === parity;
|
||||
}
|
||||
|
||||
const COUNTER_MAX = 150;
|
||||
function nextDebugIMUQuaternion(counter) {
|
||||
const angle = counter / COUNTER_MAX * 2 * Math.PI;
|
||||
const yaw = 10 * Math.PI / 180 * Math.cos(angle);
|
||||
const roll = 0;
|
||||
const pitch = 10 * Math.PI / 180 * Math.sin(angle);
|
||||
|
||||
const cy = Math.cos(yaw * 0.5);
|
||||
const sy = Math.sin(yaw * 0.5);
|
||||
const cp = Math.cos(pitch * 0.5);
|
||||
const sp = Math.sin(pitch * 0.5);
|
||||
const cr = Math.cos(roll * 0.5);
|
||||
const sr = Math.sin(roll * 0.5);
|
||||
|
||||
const w = cr * cp * cy + sr * sp * sy;
|
||||
const x = sr * cp * cy - cr * sp * sy;
|
||||
const y = cr * sp * cy + sr * cp * sy;
|
||||
const z = cr * cp * sy - sr * sp * cy;
|
||||
|
||||
return [x, y, z, w];
|
||||
}
|
||||
|
||||
export const DeviceDataStream = GObject.registerClass({
|
||||
Properties: {
|
||||
'breezy-desktop-running': GObject.ParamSpec.boolean(
|
||||
|
|
@ -72,6 +94,13 @@ export const DeviceDataStream = GObject.registerClass({
|
|||
'IMU Snapshots',
|
||||
'Latest IMU quaternion snapshots and epoch timestamp for when it was collected',
|
||||
GObject.ParamFlags.READWRITE
|
||||
),
|
||||
'debug-no-device': GObject.ParamSpec.boolean(
|
||||
'debug-no-device',
|
||||
'Debug without device',
|
||||
'Debug mode that allows for testing with moving IMU values without a device connected',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
false
|
||||
)
|
||||
}
|
||||
}, class DeviceDataStream extends GObject.Object {
|
||||
|
|
@ -90,6 +119,7 @@ export const DeviceDataStream = GObject.registerClass({
|
|||
|
||||
stop() {
|
||||
this._running = false;
|
||||
this.device_data = null;
|
||||
}
|
||||
|
||||
// polling is just intended to keep breezy_desktop_running current, anything needing up-to-date imu data should
|
||||
|
|
@ -104,6 +134,47 @@ export const DeviceDataStream = GObject.registerClass({
|
|||
// Refresh the data from the IPC file. if keepalive_only is true, we'll only check and update breezy_desktop_running if it
|
||||
// hasn't been checked within KEEPALIVE_REFRESH_INTERVAL_SEC.
|
||||
refresh_data(keepalive_only = false) {
|
||||
if (this.debug_no_device) {
|
||||
this.was_debug_no_device = true;
|
||||
if (!this.device_data) {
|
||||
this.device_data = {
|
||||
version: 1.0,
|
||||
enabled: true,
|
||||
imuResetState: false,
|
||||
displayRes: [1920.0, 1080.0],
|
||||
sbsEnabled: false,
|
||||
displayFov: 46.0,
|
||||
lookAheadCfg: [0.0, 0.0, 0.0, 0.0]
|
||||
}
|
||||
}
|
||||
|
||||
if (!keepalive_only) {
|
||||
this._counter = ((this._counter ?? -1)+1)%COUNTER_MAX;
|
||||
|
||||
const imuDataFirst = nextDebugIMUQuaternion(this._counter);
|
||||
const imuData = [
|
||||
...imuDataFirst,
|
||||
...imuDataFirst,
|
||||
...imuDataFirst,
|
||||
2.0, 1.0, 0.0, 0.0
|
||||
]
|
||||
const imuDateMs = Date.now();
|
||||
this.device_data.imuData = imuData;
|
||||
this.device_data.imuDateMs = imuDateMs;
|
||||
this.imu_snapshots = {
|
||||
imu_data: imuData,
|
||||
timestamp_ms: imuDateMs
|
||||
};
|
||||
}
|
||||
this.breezy_desktop_running = true;
|
||||
return;
|
||||
} else if (this.was_debug_no_device) {
|
||||
this.was_debug_no_device = false;
|
||||
this.device_data = null;
|
||||
this.breezy_desktop_running = false;
|
||||
this.imu_snapshots = null;
|
||||
}
|
||||
|
||||
if (this._ipc_file.query_exists(null) && (
|
||||
!this.device_data?.imuData ||
|
||||
!keepalive_only ||
|
||||
|
|
@ -114,9 +185,9 @@ export const DeviceDataStream = GObject.registerClass({
|
|||
let buffer = new Uint8Array(data[1]).buffer;
|
||||
let dataView = new DataView(buffer);
|
||||
if (dataView.byteLength === DATA_VIEW_LENGTH) {
|
||||
const imuDateMs = dataViewBigUint(dataView, EPOCH_MS);
|
||||
let imuDateMs = dataViewBigUint(dataView, EPOCH_MS);
|
||||
const validKeepalive = isValidKeepAlive(toSec(imuDateMs));
|
||||
const imuData = dataViewFloatArray(dataView, IMU_QUAT_DATA);
|
||||
let imuData = dataViewFloatArray(dataView, IMU_QUAT_DATA);
|
||||
const imuResetState = validKeepalive && imuData[0] === 0.0 && imuData[1] === 0.0 && imuData[2] === 0.0 && imuData[3] === 1.0;
|
||||
const version = dataViewUint8(dataView, VERSION);
|
||||
const enabled = dataViewUint8(dataView, ENABLED) !== 0 && version === DATA_LAYOUT_VERSION && validKeepalive;
|
||||
|
|
@ -132,9 +203,16 @@ export const DeviceDataStream = GObject.registerClass({
|
|||
imuResetState,
|
||||
displayRes: dataViewUint32Array(dataView, DISPLAY_RES),
|
||||
sbsEnabled,
|
||||
displayFov: dataViewFloat(dataView, DISPLAY_FOV),
|
||||
displayFov: 44.0, // dataViewFloat(dataView, DISPLAY_FOV),
|
||||
lookAheadCfg: dataViewFloatArray(dataView, LOOK_AHEAD_CFG),
|
||||
};
|
||||
} else if (keepalive_only) {
|
||||
this.device_data = {
|
||||
...this.device_data,
|
||||
imuResetState,
|
||||
enabled,
|
||||
sbsEnabled
|
||||
}
|
||||
}
|
||||
|
||||
let success = keepalive_only;
|
||||
|
|
@ -159,6 +237,8 @@ export const DeviceDataStream = GObject.registerClass({
|
|||
if (data[0]) {
|
||||
buffer = new Uint8Array(data[1]).buffer;
|
||||
dataView = new DataView(buffer);
|
||||
imuDateMs = dataViewBigUint(dataView, EPOCH_MS);
|
||||
imuData = dataViewFloatArray(dataView, IMU_QUAT_DATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,10 @@ export default class BreezyDesktopExtension extends Extension {
|
|||
}
|
||||
|
||||
if (!Globals.data_stream) {
|
||||
Globals.data_stream = new DeviceDataStream();
|
||||
Globals.data_stream = new DeviceDataStream({
|
||||
debug_no_device: this.settings.get_boolean('debug-no-device')
|
||||
});
|
||||
this.settings.bind('debug-no-device', Globals.data_stream, 'debug-no-device', Gio.SettingsBindFlags.DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -286,6 +289,7 @@ export default class BreezyDesktopExtension extends Extension {
|
|||
this._cursor_manager = new CursorManager(Main.layoutManager.uiGroup, [targetMonitor, ...virtualMonitors], refreshRate);
|
||||
this._cursor_manager.enable();
|
||||
|
||||
// use rgba(255, 4, 144, 1) for chroma key background
|
||||
this._overlay = new St.Bin({ style: 'background-color: rgba(0, 0, 0, 1);', reactive: false, clip_to_allocation: true });
|
||||
this._overlay.opacity = 255;
|
||||
this._overlay.set_position(targetMonitor.x, targetMonitor.y);
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ function degreesToRadians(degrees) {
|
|||
}
|
||||
|
||||
/***
|
||||
* @returns {Object} - containing `start`, `center`, and `end` radians for rotating the given monitor
|
||||
* @returns {Object} - containing `begin`, `center`, and `end` radians for rotating the given monitor
|
||||
*/
|
||||
function monitorWrap(cachedMonitorWrap, radiusPixels, monitorSpacingPixels, monitorBeginPixel, monitorLengthPixels) {
|
||||
let closestWrap = cachedMonitorWrap.reduce((previous, current) => {
|
||||
|
|
@ -125,17 +125,33 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorWrappingSch
|
|||
|
||||
// distance to a horizontal edge is the hypothenuse of the triangle where the opposite side is half the width of the reference fov screen
|
||||
const edgeRadius = fovDetails.widthPixels / 2 / Math.sin(fovHorizontalRadians / 2);
|
||||
const monitorSpacingPixels = monitorSpacing * fovDetails.widthPixels;
|
||||
|
||||
cachedMonitorWrap.push({ pixel: 0, radians: -fovHorizontalRadians / 2 });
|
||||
monitorDetailsList.forEach(monitorDetails => {
|
||||
const monitorWrapDetails = monitorWrap(cachedMonitorWrap, edgeRadius, monitorSpacing * fovDetails.widthPixels, monitorDetails.x, monitorDetails.width);
|
||||
const monitorCenterRadius = Math.sqrt(Math.pow(edgeRadius, 2) - Math.pow(monitorDetails.width / 2, 2))
|
||||
const monitorWrapDetails = monitorWrap(cachedMonitorWrap, edgeRadius, monitorSpacingPixels, monitorDetails.x, monitorDetails.width);
|
||||
const monitorCenterRadius = Math.sqrt(Math.pow(edgeRadius, 2) - Math.pow(monitorDetails.width / 2, 2));
|
||||
const upTopPixels = monitorDetails.y + (monitorDetails.y / fovDetails.heightPixels) * monitorSpacingPixels;
|
||||
const upCenterPixels = upTopPixels + monitorDetails.height / 2 - fovDetails.heightPixels / 2;
|
||||
|
||||
monitorPlacements.push({
|
||||
topLeftNoRotate: [
|
||||
monitorCenterRadius,
|
||||
|
||||
// west stays aligned with (0, 0), will apply rotationAngleRadians value during rendering
|
||||
-(monitorDetails.width - fovDetails.widthPixels) / 2,
|
||||
-monitorDetails.y
|
||||
|
||||
// up is flat when wrapping horizontally, apply it here as a constant, not touched by rendering
|
||||
-upTopPixels
|
||||
],
|
||||
centerNoRotate: [
|
||||
monitorCenterRadius,
|
||||
|
||||
// west centered about the FOV center
|
||||
0,
|
||||
|
||||
// up is flat when wrapping horizontally
|
||||
-upCenterPixels
|
||||
],
|
||||
center: [
|
||||
// north is adjacent where radius is the hypotenuse, using monitorWrapDetails.center as the radians
|
||||
|
|
@ -145,7 +161,7 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorWrappingSch
|
|||
-monitorCenterRadius * Math.sin(monitorWrapDetails.center),
|
||||
|
||||
// up is flat when wrapping horizontally
|
||||
-(monitorDetails.y + monitorDetails.height / 2 - fovDetails.heightPixels / 2)
|
||||
-upCenterPixels
|
||||
],
|
||||
rotationAngleRadians: {
|
||||
x: 0,
|
||||
|
|
@ -158,24 +174,40 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorWrappingSch
|
|||
|
||||
// distance to a vertical edge is the hypothenuse of the triangle where the opposite side is half the height of the reference fov screen
|
||||
const edgeRadius = fovDetails.heightPixels / 2 / Math.sin(fovVerticalRadians / 2);
|
||||
const monitorSpacingPixels = monitorSpacing * fovDetails.heightPixels;
|
||||
|
||||
cachedMonitorWrap.push({ pixel: 0, radians: -fovVerticalRadians / 2 });
|
||||
monitorDetailsList.forEach(monitorDetails => {
|
||||
const monitorWrapDetails = monitorWrap(cachedMonitorWrap, edgeRadius, monitorSpacing * fovDetails.heightPixels, monitorDetails.y, monitorDetails.height);
|
||||
const monitorCenterRadius = Math.sqrt(Math.pow(edgeRadius, 2) - Math.pow(monitorDetails.height / 2, 2)) ;
|
||||
const monitorWrapDetails = monitorWrap(cachedMonitorWrap, edgeRadius, monitorSpacingPixels, monitorDetails.y, monitorDetails.height);
|
||||
const monitorCenterRadius = Math.sqrt(Math.pow(edgeRadius, 2) - Math.pow(monitorDetails.height / 2, 2));
|
||||
const westPixels = monitorDetails.x + (monitorDetails.x / fovDetails.widthPixels) * monitorSpacingPixels;
|
||||
const westCenterPixels = westPixels + monitorDetails.width / 2 - fovDetails.widthPixels / 2;
|
||||
|
||||
monitorPlacements.push({
|
||||
topLeftNoRotate: [
|
||||
monitorCenterRadius,
|
||||
monitorDetails.x,
|
||||
|
||||
// west is flat when wrapping vertically, apply it here as a constant, not touched by rendering
|
||||
westPixels,
|
||||
|
||||
// up stays aligned with (0, 0), will apply rotationAngleRadians value during rendering
|
||||
(monitorDetails.height - fovDetails.heightPixels) / 2
|
||||
],
|
||||
centerNoRotate: [
|
||||
monitorCenterRadius,
|
||||
|
||||
// west is flat when wrapping horizontally
|
||||
westCenterPixels,
|
||||
|
||||
// west centered about the FOV center
|
||||
0
|
||||
],
|
||||
center: [
|
||||
// north is adjacent where radius is the hypotenuse, using monitorWrapDetails.center as the radians
|
||||
monitorCenterRadius * Math.cos(monitorWrapDetails.center),
|
||||
|
||||
// west is flat when wrapping vertically
|
||||
-(monitorDetails.x + monitorDetails.width / 2 - fovDetails.widthPixels / 2),
|
||||
-westCenterPixels,
|
||||
|
||||
// up is opposite where radius is the hypotenuse, using monitorWrapDetails.center as the radians
|
||||
-monitorCenterRadius * Math.sin(monitorWrapDetails.center)
|
||||
|
|
@ -187,18 +219,29 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorWrappingSch
|
|||
});
|
||||
});
|
||||
} else {
|
||||
const monitorSpacingPixels = monitorSpacing * fovDetails.widthPixels;
|
||||
|
||||
// monitors make a flat wall in front of us, no wrapping
|
||||
monitorDetailsList.forEach(monitorDetails => {
|
||||
const upPixels = monitorDetails.y + (monitorDetails.y / fovDetails.heightPixels) * monitorSpacingPixels;
|
||||
const westPixels = monitorDetails.x + (monitorDetails.x / fovDetails.widthPixels) * monitorSpacingPixels;
|
||||
const westCenterPixels = westPixels + monitorDetails.width / 2 - fovDetails.widthPixels / 2;
|
||||
const upCenterPixels = upPixels + monitorDetails.height / 2 - fovDetails.heightPixels / 2;
|
||||
monitorPlacements.push({
|
||||
topLeftNoRotate: [
|
||||
centerRadius,
|
||||
monitorDetails.x,
|
||||
-monitorDetails.y
|
||||
westPixels,
|
||||
-upPixels
|
||||
],
|
||||
centerNoRotate: [
|
||||
centerRadius,
|
||||
westCenterPixels,
|
||||
-upCenterPixels
|
||||
],
|
||||
center: [
|
||||
centerRadius,
|
||||
-(monitorDetails.x + monitorDetails.width / 2 - fovDetails.widthPixels / 2),
|
||||
-(monitorDetails.y + monitorDetails.height / 2 - fovDetails.heightPixels / 2)
|
||||
-westCenterPixels,
|
||||
-upCenterPixels
|
||||
],
|
||||
rotationAngleRadians: {
|
||||
x: 0,
|
||||
|
|
@ -387,12 +430,17 @@ export const VirtualMonitorEffect = GObject.registerClass({
|
|||
|
||||
_update_display_position_uniforms() {
|
||||
// this is in NWU coordinates
|
||||
const noRotationVector = this.monitor_placements[this.monitor_index].topLeftNoRotate;
|
||||
Globals.logger.log_debug(`\t\t\tMonitor ${this.monitor_index} vectors: ${JSON.stringify(this.monitor_placements[this.monitor_index])}`);
|
||||
const monitorPlacement = this.monitor_placements[this.monitor_index];
|
||||
// Globals.logger.log_debug(`\t\t\tMonitor ${this.monitor_index} vectors: ${JSON.stringify(monitorPlacement)}`);
|
||||
|
||||
// use the center vector with the distance applied to determine how much to move each coordinate, so they all move uniformly
|
||||
const inverseAppliedDistance = 1.0 - this._current_display_distance / this.display_distance_default;
|
||||
const distanceDelta = monitorPlacement.centerNoRotate.map(coord => coord * inverseAppliedDistance);
|
||||
const noRotationVector = monitorPlacement.topLeftNoRotate.map((coord, index) => coord - distanceDelta[index]);
|
||||
|
||||
// convert to CoGL's east-down-south coordinates and apply display distance
|
||||
this.set_uniform_float(this.get_uniform_location("u_display_position"), 3,
|
||||
[-noRotationVector[1], -noRotationVector[2], this._current_display_distance / this.display_distance_default * -noRotationVector[0]]);
|
||||
[-noRotationVector[1], -noRotationVector[2], -noRotationVector[0]]);
|
||||
|
||||
const rotation_radians = this.monitor_placements[this.monitor_index].rotationAngleRadians;
|
||||
this.set_uniform_float(this.get_uniform_location("u_rotation_x_radians"), 1, [rotation_radians.x]);
|
||||
|
|
|
|||
|
|
@ -208,6 +208,15 @@
|
|||
Log debug messages
|
||||
</description>
|
||||
</key>
|
||||
<key name="debug-no-device" type="b">
|
||||
<default>
|
||||
false
|
||||
</default>
|
||||
<summary>Debug no device</summary>
|
||||
<description>
|
||||
Debug no device
|
||||
</description>
|
||||
</key>
|
||||
<key name="custom-monitor-product" type="s">
|
||||
<default>
|
||||
""
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ from .extensionsmanager import ExtensionsManager
|
|||
from .license import BREEZY_GNOME_FEATURES
|
||||
from .licensedialog import LicenseDialog
|
||||
from .statemanager import StateManager
|
||||
from .settingsmanager import SettingsManager
|
||||
from .connecteddevice import ConnectedDevice
|
||||
from .failedverification import FailedVerification
|
||||
from .nodevice import NoDevice
|
||||
|
|
@ -45,11 +46,13 @@ class BreezydesktopWindow(Gtk.ApplicationWindow):
|
|||
|
||||
self._skip_verification = skip_verification
|
||||
|
||||
self.settings = SettingsManager.get_instance().settings
|
||||
self.state_manager = StateManager.get_instance()
|
||||
self.state_manager.connect('device-update', self._handle_state_update)
|
||||
self.state_manager.connect('notify::license-action-needed', self._handle_state_update)
|
||||
self.state_manager.connect('notify::license-present', self._handle_state_update)
|
||||
self.state_manager.connect('notify::enabled-features-list', self._handle_state_update)
|
||||
self.settings.connect('changed::debug-no-device', self._handle_settings_update)
|
||||
|
||||
self.connected_device = ConnectedDevice()
|
||||
self.failed_verification = FailedVerification()
|
||||
|
|
@ -67,6 +70,9 @@ class BreezydesktopWindow(Gtk.ApplicationWindow):
|
|||
|
||||
self.connect("destroy", self._on_window_destroy)
|
||||
|
||||
def _handle_settings_update(self, settings_manager, key):
|
||||
self._handle_state_update(self.state_manager, None)
|
||||
|
||||
def _handle_state_update(self, state_manager, val):
|
||||
GLib.idle_add(self._handle_state_update_gui, state_manager)
|
||||
|
||||
|
|
@ -83,6 +89,9 @@ class BreezydesktopWindow(Gtk.ApplicationWindow):
|
|||
self.main_content.append(self.failed_verification)
|
||||
elif not ExtensionsManager.get_instance().is_installed():
|
||||
self.main_content.append(self.no_extension)
|
||||
elif self.settings.get_boolean('debug-no-device'):
|
||||
self.main_content.append(self.connected_device)
|
||||
self.connected_device.set_device_name('Fake device')
|
||||
elif not self.state_manager.driver_running:
|
||||
self.main_content.append(self.no_driver)
|
||||
elif not self.state_manager.license_present:
|
||||
|
|
|
|||
Loading…
Reference in New Issue