diff --git a/VERSION b/VERSION index 58073ef..fad066f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.4.1 \ No newline at end of file +2.5.0 \ No newline at end of file diff --git a/gnome/src/devicedatastream.js b/gnome/src/devicedatastream.js index e396403..4d88ff7 100644 --- a/gnome/src/devicedatastream.js +++ b/gnome/src/devicedatastream.js @@ -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] }; diff --git a/gnome/src/virtualdisplayeffect.js b/gnome/src/virtualdisplayeffect.js index 0cb8a16..f4b29f5 100644 --- a/gnome/src/virtualdisplayeffect.js +++ b/gnome/src/virtualdisplayeffect.js @@ -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)]); diff --git a/gnome/src/virtualdisplaysactor.js b/gnome/src/virtualdisplaysactor.js index 0a27948..4f7d7db 100644 --- a/gnome/src/virtualdisplaysactor.js +++ b/gnome/src/virtualdisplaysactor.js @@ -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(), diff --git a/modules/XRLinuxDriver b/modules/XRLinuxDriver index f32a295..d3b903e 160000 --- a/modules/XRLinuxDriver +++ b/modules/XRLinuxDriver @@ -1 +1 @@ -Subproject commit f32a2951d6ce8d97b528281b6bab78c654020e01 +Subproject commit d3b903eb33b09cc13a93ee51463c3b3fcb2840b6 diff --git a/ui/modules/PyXRLinuxDriverIPC b/ui/modules/PyXRLinuxDriverIPC index 0eb04ff..da173bd 160000 --- a/ui/modules/PyXRLinuxDriverIPC +++ b/ui/modules/PyXRLinuxDriverIPC @@ -1 +1 @@ -Subproject commit 0eb04ff4429ce7a025f126843cd0d3b24bc0d73e +Subproject commit da173bd9e0392aaeb2cb68a332e5d4a20dd4dae1