Update Breezy GNOME to support 6DoF position

This commit is contained in:
wheaney 2025-10-22 11:55:22 -07:00
parent c68ab16ef1
commit e31778f6cc
6 changed files with 53 additions and 30 deletions

View File

@ -1 +1 @@
2.4.1
2.5.0

View File

@ -21,7 +21,7 @@ const IPC_FILE_PATH = "/dev/shm/breezy_desktop_imu";
const KEEPALIVE_REFRESH_INTERVAL_SEC = 1;
// the driver should be using the same data layout version
const DATA_LAYOUT_VERSION = 4;
const DATA_LAYOUT_VERSION = 5;
// DataView info: [offset, size, count]
const VERSION = [0, UINT8_SIZE, 1];
@ -34,16 +34,17 @@ const SBS_ENABLED = [dataViewEnd(LENS_DISTANCE_RATIO), BOOL_SIZE, 1];
const CUSTOM_BANNER_ENABLED = [dataViewEnd(SBS_ENABLED), BOOL_SIZE, 1];
const SMOOTH_FOLLOW_ENABLED = [dataViewEnd(CUSTOM_BANNER_ENABLED), BOOL_SIZE, 1];
const SMOOTH_FOLLOW_ORIGIN_DATA = [dataViewEnd(SMOOTH_FOLLOW_ENABLED), FLOAT_SIZE, 16];
const EPOCH_MS = [dataViewEnd(SMOOTH_FOLLOW_ORIGIN_DATA), UINT_SIZE, 2];
const IMU_QUAT_DATA = [dataViewEnd(EPOCH_MS), FLOAT_SIZE, 16];
const IMU_PARITY_BYTE = [dataViewEnd(IMU_QUAT_DATA), UINT8_SIZE, 1];
const POSE_POSITION = [dataViewEnd(SMOOTH_FOLLOW_ORIGIN_DATA), FLOAT_SIZE, 3];
const EPOCH_MS = [dataViewEnd(POSE_POSITION), UINT_SIZE, 2];
const POSE_ORIENTATION = [dataViewEnd(EPOCH_MS), FLOAT_SIZE, 16];
const IMU_PARITY_BYTE = [dataViewEnd(POSE_ORIENTATION), UINT8_SIZE, 1];
const DATA_VIEW_LENGTH = dataViewEnd(IMU_PARITY_BYTE);
function checkParityByte(dataView) {
const parityByte = dataViewUint8(dataView, IMU_PARITY_BYTE);
let parity = 0;
const epochUint8 = dataViewUint8Array(dataView, EPOCH_MS);
const imuDataUint8 = dataViewUint8Array(dataView, IMU_QUAT_DATA);
const imuDataUint8 = dataViewUint8Array(dataView, POSE_ORIENTATION);
for (let i = 0; i < epochUint8.length; i++) {
parity ^= epochUint8[i];
}
@ -210,10 +211,11 @@ export const DeviceDataStream = GObject.registerClass({
const validData = validKeepAlive && displayFov !== 0.0;
const version = dataViewUint8(dataView, VERSION);
const enabled = dataViewUint8(dataView, ENABLED) !== 0 && version === DATA_LAYOUT_VERSION && validData;
let imuData = dataViewFloatArray(dataView, IMU_QUAT_DATA);
let poseOrientation = dataViewFloatArray(dataView, POSE_ORIENTATION);
let posePosition = dataViewFloatArray(dataView, POSE_POSITION);
let smoothFollowEnabled = !this.legacy_follow_mode && dataViewUint8(dataView, SMOOTH_FOLLOW_ENABLED) !== 0;
let smoothFollowOrigin = dataViewFloatArray(dataView, SMOOTH_FOLLOW_ORIGIN_DATA);
const imuResetState = enabled && validData && imuData[0] === 0.0 && imuData[1] === 0.0 && imuData[2] === 0.0 && imuData[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 sbsEnabled = dataViewUint8(dataView, SBS_ENABLED) !== 0;
@ -261,7 +263,8 @@ export const DeviceDataStream = GObject.registerClass({
if (checkParityByte(dataView)) {
this.imu_snapshots = {
imu_data: imuData,
pose_orientation: poseOrientation,
pose_position: posePosition,
timestamp_ms: imuDateMs,
smooth_follow_origin: smoothFollowOrigin
};
@ -277,7 +280,8 @@ export const DeviceDataStream = GObject.registerClass({
buffer = new Uint8Array(data[1]).buffer;
dataView = new DataView(buffer);
imuDateMs = dataViewBigUint(dataView, EPOCH_MS);
imuData = dataViewFloatArray(dataView, IMU_QUAT_DATA);
poseOrientation = dataViewFloatArray(dataView, POSE_ORIENTATION);
posePosition = dataViewFloatArray(dataView, POSE_POSITION);
}
}
}
@ -311,15 +315,17 @@ export const DeviceDataStream = GObject.registerClass({
if (!keepalive_only) {
this._counter = ((this._counter ?? -1)+1)%COUNTER_MAX;
const imuDataFirst = nextDebugIMUQuaternion(this._counter);
const imuData = [
...imuDataFirst,
...imuDataFirst,
...imuDataFirst,
const poseOrientationFirst = nextDebugIMUQuaternion(this._counter);
const poseOrientation = [
...poseOrientationFirst,
...poseOrientationFirst,
...poseOrientationFirst,
2.0, 1.0, 0.0, 0.0
]
const posePosition = [0.0, 0.0, 0.0];
this.imu_snapshots = {
imu_data: imuData,
pose_orientation: poseOrientation,
pose_position: posePosition,
timestamp_ms: Date.now(),
smooth_follow_origin: [0.0, 0.0, 0.0, 1.0]
};

View File

@ -417,7 +417,8 @@ export const VirtualDisplayEffect = GObject.registerClass({
vfunc_build_pipeline() {
const declarations = `
uniform bool u_show_banner;
uniform mat4 u_imu_data;
uniform mat4 u_pose_orientation;
uniform vec3 u_pose_position;
uniform float u_look_ahead_ms;
uniform vec4 u_look_ahead_cfg;
uniform mat4 u_projection_matrix;
@ -460,7 +461,11 @@ export const VirtualDisplayEffect = GObject.registerClass({
vec4 nwuToESU(vec4 v) {
return vec4(-v.y, v.z, -v.x, v.w);
}
vec3 nwuToESU(vec3 v) {
return vec3(-v.y, v.z, -v.x);
}
// returns the rate of change between the two vectors, in same time units as delta_time
// e.g. if delta_time is in ms, then the rate of change is "per ms"
vec3 rateOfChange(vec3 v1, vec3 v2, float delta_time) {
@ -487,14 +492,23 @@ export const VirtualDisplayEffect = GObject.registerClass({
if (!u_show_banner) {
float aspect_ratio = u_display_resolution.x / u_display_resolution.y;
vec4 quat_t0 = nwuToESU(quatConjugate(u_pose_orientation[0]));
vec3 position_vector = applyQuaternionToVector(nwuToESU(u_pose_position), quat_t0);
vec3 final_lens_position = u_lens_vector + position_vector;
vec3 complete_vector = applyXRotationToVector(world_pos.xyz, u_rotation_x_radians);
complete_vector = applyYRotationToVector(complete_vector, u_rotation_y_radians);
vec4 quat_t0 = nwuToESU(quatConjugate(u_imu_data[0]));
vec3 rotated_vector_t0 = applyQuaternionToVector(complete_vector, quat_t0);
vec3 rotated_vector_t1 = applyQuaternionToVector(complete_vector, nwuToESU(quatConjugate(u_imu_data[1])));
float delta_time_t0 = u_imu_data[3][0] - u_imu_data[3][1];
vec3 velocity_t0 = rateOfChange(rotated_vector_t0, rotated_vector_t1, delta_time_t0);
vec3 rotated_vector_t1 = applyQuaternionToVector(complete_vector, nwuToESU(quatConjugate(u_pose_orientation[1])));
float delta_time_t0 = u_pose_orientation[3][0] - u_pose_orientation[3][1];
// how quickly the vertex is moving relative to the camera
vec3 velocity_t0 = rateOfChange(
rotated_vector_t0 - final_lens_position,
rotated_vector_t1 - final_lens_position,
delta_time_t0
);
// compute the capped look ahead with scanline adjustments
float look_ahead_scanline_ms = u_look_ahead_ms == 0.0 ? 0.0 : vectorToScanline(u_fov_vertical_radians, rotated_vector_t0) * u_look_ahead_cfg[2];
@ -502,7 +516,7 @@ export const VirtualDisplayEffect = GObject.registerClass({
vec3 look_ahead_vector = applyLookAhead(rotated_vector_t0, velocity_t0, effective_look_ahead_ms);
world_pos = vec4(look_ahead_vector - u_lens_vector, world_pos.w);
world_pos = vec4(look_ahead_vector - final_lens_position, world_pos.w);
world_pos.z /= aspect_ratio / u_actor_to_display_ratios.y;
@ -557,9 +571,12 @@ export const VirtualDisplayEffect = GObject.registerClass({
this.set_uniform_float(this.get_uniform_location('u_look_ahead_ms'), 1, [0.0]);
lookAheadSet = true;
}
this.set_uniform_matrix(this.get_uniform_location("u_imu_data"), false, 4, this.imu_snapshots.imu_data);
const posePositionPixels = this.imu_snapshots.pose_position.map(coord => coord * this.fov_details.completeScreenDistancePixels);
this.set_uniform_matrix(this.get_uniform_location("u_pose_orientation"), false, 4, this.imu_snapshots.pose_orientation);
this.set_uniform_float(this.get_uniform_location("u_pose_position"), 3, posePositionPixels);
} else {
this.set_uniform_matrix(this.get_uniform_location("u_imu_data"), false, 4, this.imu_snapshots.smooth_follow_origin);
this.set_uniform_matrix(this.get_uniform_location("u_pose_orientation"), false, 4, this.imu_snapshots.smooth_follow_origin);
this.set_uniform_float(this.get_uniform_location("u_pose_position"), 3, [0.0, 0.0, 0.0]);
}
if (!lookAheadSet) {
this.set_uniform_float(this.get_uniform_location('u_look_ahead_ms'), 1, [lookAheadMS(this.imu_snapshots.timestamp_ms, Globals.data_stream.device_data.lookAheadCfg, this.look_ahead_override)]);

View File

@ -785,12 +785,12 @@ export const VirtualDisplaysActor = GObject.registerClass({
(!this._smooth_follow_slerping || this.focused_monitor_index === -1)) {
// if smooth follow is enabled, use the origin IMU data to inform the initial focused monitor
// since it reflects where the user is looking in relation to the original monitor positions
const currentPoseQuat = this.smooth_follow_enabled ?
const currentOrientationQuat = this.smooth_follow_enabled ?
this.imu_snapshots.smooth_follow_origin.splice(0, 4) :
this.imu_snapshots.imu_data.splice(0, 4);
this.imu_snapshots.pose_orientation.splice(0, 4);
const focusedMonitorIndex = findFocusedMonitor(
currentPoseQuat,
currentOrientationQuat,
this.monitor_placements.map(monitorVectors => monitorVectors.centerLook),
this.focused_monitor_index,
this.display_distance / this._display_distance_default(),

@ -1 +1 @@
Subproject commit f32a2951d6ce8d97b528281b6bab78c654020e01
Subproject commit d3b903eb33b09cc13a93ee51463c3b3fcb2840b6

@ -1 +1 @@
Subproject commit 0eb04ff4429ce7a025f126843cd0d3b24bc0d73e
Subproject commit da173bd9e0392aaeb2cb68a332e5d4a20dd4dae1