Fix display placement issues
This commit is contained in:
parent
f980d0bf2b
commit
a0527a0944
|
|
@ -1,3 +1,20 @@
|
||||||
export function degreeToRadian(degree) {
|
export function degreeToRadian(degree) {
|
||||||
return degree * Math.PI / 180;
|
return degree * Math.PI / 180;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FOV in radians is spherical, so doesn't follow Pythagoras' theorem
|
||||||
|
export function diagonalToCrossFOVs(diagonalFOVRadians, aspectRatio) {
|
||||||
|
// first convert from a spherical FOV to a diagonal FOV on a flat plane at a generic distance of 1.0
|
||||||
|
const flatDiagonalFOV = 2 * Math.tan(diagonalFOVRadians / 2);
|
||||||
|
|
||||||
|
// then convert to flat plane horizontal and vertical FOVs
|
||||||
|
const flatVerticalFOV = flatDiagonalFOV / Math.sqrt(1 + aspectRatio * aspectRatio);
|
||||||
|
const flatHorizontalFOV = flatVerticalFOV * aspectRatio;
|
||||||
|
|
||||||
|
// then convert back to spherical FOV
|
||||||
|
return {
|
||||||
|
diagonal: diagonalFOVRadians,
|
||||||
|
horizontal: 2 * Math.atan(Math.tan(flatHorizontalFOV / 2)),
|
||||||
|
vertical: 2 * Math.atan(Math.tan(flatVerticalFOV / 2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import GObject from 'gi://GObject';
|
||||||
import Shell from 'gi://Shell';
|
import Shell from 'gi://Shell';
|
||||||
|
|
||||||
import Globals from './globals.js';
|
import Globals from './globals.js';
|
||||||
|
import { degreeToRadian, diagonalToCrossFOVs } from './math.js';
|
||||||
|
|
||||||
// how far to look ahead is how old the IMU data is plus a constant that is either the default for this device or an override
|
// how far to look ahead is how old the IMU data is plus a constant that is either the default for this device or an override
|
||||||
function lookAheadMS(imuDateMs, lookAheadCfg, override) {
|
function lookAheadMS(imuDateMs, lookAheadCfg, override) {
|
||||||
|
|
@ -236,8 +237,7 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
||||||
this.set_uniform_float(this.get_uniform_location("u_show_banner"), 1, [this.show_banner ? 1.0 : 0.0]);
|
this.set_uniform_float(this.get_uniform_location("u_show_banner"), 1, [this.show_banner ? 1.0 : 0.0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
perspective(fovVerticalRadians, aspect, near, far) {
|
perspective(fovHorizontalRadians, aspect, near, far) {
|
||||||
const fovHorizontalRadians = fovVerticalRadians * aspect;
|
|
||||||
const f = 1.0 / Math.tan(fovHorizontalRadians / 2.0);
|
const f = 1.0 / Math.tan(fovHorizontalRadians / 2.0);
|
||||||
const range = far - near;
|
const range = far - near;
|
||||||
|
|
||||||
|
|
@ -383,17 +383,15 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
||||||
vfunc_paint_target(node, paintContext) {
|
vfunc_paint_target(node, paintContext) {
|
||||||
if (!this._initialized) {
|
if (!this._initialized) {
|
||||||
const aspect = this.target_monitor.width / this.target_monitor.height;
|
const aspect = this.target_monitor.width / this.target_monitor.height;
|
||||||
const fovDiagonalRadians = Globals.data_stream.device_data.displayFov * Math.PI / 180.0;
|
const fovRadians = diagonalToCrossFOVs(degreeToRadian(Globals.data_stream.device_data.displayFov), aspect);
|
||||||
const diagToVertRatio = Math.sqrt(aspect * aspect + 1);
|
|
||||||
const fovVerticalRadians = fovDiagonalRadians / diagToVertRatio;
|
|
||||||
const projection_matrix = this.perspective(
|
const projection_matrix = this.perspective(
|
||||||
fovVerticalRadians,
|
fovRadians.horizontal,
|
||||||
aspect,
|
aspect,
|
||||||
0.0001,
|
0.0001,
|
||||||
1000.0
|
1000.0
|
||||||
);
|
);
|
||||||
this.set_uniform_matrix(this.get_uniform_location("u_projection_matrix"), false, 4, projection_matrix);
|
this.set_uniform_matrix(this.get_uniform_location("u_projection_matrix"), false, 4, projection_matrix);
|
||||||
this.set_uniform_float(this.get_uniform_location("u_fov_vertical_radians"), 1, [fovVerticalRadians]);
|
this.set_uniform_float(this.get_uniform_location("u_fov_vertical_radians"), 1, [fovRadians.vertical]);
|
||||||
this.set_uniform_float(this.get_uniform_location("u_display_resolution"), 2, [this.target_monitor.width, this.target_monitor.height]);
|
this.set_uniform_float(this.get_uniform_location("u_display_resolution"), 2, [this.target_monitor.width, this.target_monitor.height]);
|
||||||
this.set_uniform_float(this.get_uniform_location("u_look_ahead_cfg"), 4, Globals.data_stream.device_data.lookAheadCfg);
|
this.set_uniform_float(this.get_uniform_location("u_look_ahead_cfg"), 4, Globals.data_stream.device_data.lookAheadCfg);
|
||||||
this.set_uniform_float(this.get_uniform_location("u_actor_to_display_ratios"), 2, this.actor_to_display_ratios);
|
this.set_uniform_float(this.get_uniform_location("u_actor_to_display_ratios"), 2, this.actor_to_display_ratios);
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import Shell from 'gi://Shell';
|
||||||
import St from 'gi://St';
|
import St from 'gi://St';
|
||||||
|
|
||||||
import { VirtualDisplayEffect } from './virtualdisplayeffect.js';
|
import { VirtualDisplayEffect } from './virtualdisplayeffect.js';
|
||||||
|
import { degreeToRadian, diagonalToCrossFOVs } from './math.js';
|
||||||
|
|
||||||
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||||
|
|
||||||
|
|
@ -84,16 +85,16 @@ function findFocusedMonitor(quaternion, monitorVectors, currentFocusedIndex, foc
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
const distancePixels = fovDetails.completeScreenDistance * Math.tan(distance);
|
const distanceFromCenterPixels = fovDetails.completeScreenDistancePixels * Math.tan(distance);
|
||||||
const distanceToMonitorSize = distancePixels / monitor.width;
|
const distanceFromCenterSizeRatio = distanceFromCenterPixels / monitor.width;
|
||||||
|
|
||||||
if (currentFocusedIndex === index) {
|
if (currentFocusedIndex === index) {
|
||||||
currentFocusedDistance = distanceToMonitorSize * focusedMonitorDistance;
|
currentFocusedDistance = distanceFromCenterSizeRatio * focusedMonitorDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (distanceToMonitorSize < closestDistance) {
|
if (distanceFromCenterSizeRatio < closestDistance) {
|
||||||
closestIndex = index;
|
closestIndex = index;
|
||||||
closestDistance = distanceToMonitorSize;
|
closestDistance = distanceFromCenterSizeRatio;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -108,10 +109,6 @@ function findFocusedMonitor(quaternion, monitorVectors, currentFocusedIndex, foc
|
||||||
return currentFocusedIndex;
|
return currentFocusedIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
function degreesToRadians(degrees) {
|
|
||||||
return degrees * Math.PI / 180.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* @returns {Object} - containing `begin`, `center`, and `end` radians for rotating the given monitor
|
* @returns {Object} - containing `begin`, `center`, and `end` radians for rotating the given monitor
|
||||||
*/
|
*/
|
||||||
|
|
@ -195,18 +192,19 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorWrappingSch
|
||||||
// monitors wrap around us horizontally
|
// monitors wrap around us horizontally
|
||||||
|
|
||||||
// distance to a horizontal edge is the hypothenuse of the triangle where the opposite side is half the width of the reference fov screen
|
// 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 sideEdgeRadius = Math.sqrt(Math.pow(fovDetails.widthPixels / 2, 2) + Math.pow(fovDetails.completeScreenDistance, 2));
|
const sideEdgeRadius = Math.sqrt(Math.pow(fovDetails.widthPixels / 2, 2) + Math.pow(fovDetails.completeScreenDistancePixels, 2));
|
||||||
const monitorSpacingPixels = monitorSpacing * fovDetails.widthPixels;
|
const monitorSpacingPixels = monitorSpacing * fovDetails.widthPixels;
|
||||||
|
|
||||||
cachedMonitorRadians[0] = -fovDetails.horizontalRadians / 2;
|
cachedMonitorRadians[0] = -fovDetails.defaultDistanceHorizontalRadians / 2;
|
||||||
monitorDetailsList.forEach(monitorDetails => {
|
monitorDetailsList.forEach(monitorDetails => {
|
||||||
const monitorWrapDetails = monitorWrap(cachedMonitorRadians, sideEdgeRadius, monitorSpacingPixels, monitorDetails.x, monitorDetails.width);
|
const monitorWrapDetails = monitorWrap(cachedMonitorRadians, sideEdgeRadius, monitorSpacingPixels, monitorDetails.x, monitorDetails.width);
|
||||||
|
const monitorCenterRadius = Math.sqrt(Math.pow(sideEdgeRadius, 2) - Math.pow(monitorDetails.width / 2, 2));
|
||||||
const upTopPixels = monitorDetails.y + (monitorDetails.y / fovDetails.heightPixels) * monitorSpacingPixels;
|
const upTopPixels = monitorDetails.y + (monitorDetails.y / fovDetails.heightPixels) * monitorSpacingPixels;
|
||||||
const upCenterPixels = upTopPixels + monitorDetails.height / 2 - fovDetails.heightPixels / 2;
|
const upCenterPixels = upTopPixels + monitorDetails.height / 2 - fovDetails.heightPixels / 2;
|
||||||
|
|
||||||
monitorPlacements.push({
|
monitorPlacements.push({
|
||||||
topLeftNoRotate: [
|
topLeftNoRotate: [
|
||||||
fovDetails.completeScreenDistance,
|
monitorCenterRadius,
|
||||||
|
|
||||||
// west stays aligned with (0, 0), will apply rotationAngleRadians value during rendering
|
// west stays aligned with (0, 0), will apply rotationAngleRadians value during rendering
|
||||||
-(monitorDetails.width - fovDetails.widthPixels) / 2,
|
-(monitorDetails.width - fovDetails.widthPixels) / 2,
|
||||||
|
|
@ -215,7 +213,7 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorWrappingSch
|
||||||
-upTopPixels
|
-upTopPixels
|
||||||
],
|
],
|
||||||
centerNoRotate: [
|
centerNoRotate: [
|
||||||
fovDetails.completeScreenDistance,
|
monitorCenterRadius,
|
||||||
|
|
||||||
// west centered about the FOV center
|
// west centered about the FOV center
|
||||||
0,
|
0,
|
||||||
|
|
@ -225,10 +223,10 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorWrappingSch
|
||||||
],
|
],
|
||||||
center: [
|
center: [
|
||||||
// north is adjacent where radius is the hypotenuse, using monitorWrapDetails.center as the radians
|
// north is adjacent where radius is the hypotenuse, using monitorWrapDetails.center as the radians
|
||||||
fovDetails.completeScreenDistance * Math.cos(monitorWrapDetails.center),
|
monitorCenterRadius * Math.cos(monitorWrapDetails.center),
|
||||||
|
|
||||||
// west is opposite where radius is the hypotenuse, using monitorWrapDetails.center as the radians
|
// west is opposite where radius is the hypotenuse, using monitorWrapDetails.center as the radians
|
||||||
-fovDetails.completeScreenDistance * Math.sin(monitorWrapDetails.center),
|
-monitorCenterRadius * Math.sin(monitorWrapDetails.center),
|
||||||
|
|
||||||
// up is flat when wrapping horizontally
|
// up is flat when wrapping horizontally
|
||||||
-upCenterPixels
|
-upCenterPixels
|
||||||
|
|
@ -243,18 +241,19 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorWrappingSch
|
||||||
// monitors wrap around us vertically
|
// monitors wrap around us vertically
|
||||||
|
|
||||||
// distance to the top edge is the hypothenuse of the triangle where the opposite side is half the height of the reference fov screen
|
// distance to the top edge is the hypothenuse of the triangle where the opposite side is half the height of the reference fov screen
|
||||||
const topEdgeRadius = Math.sqrt(Math.pow(fovDetails.heightPixels / 2, 2) + Math.pow(fovDetails.completeScreenDistance, 2));
|
const topEdgeRadius = Math.sqrt(Math.pow(fovDetails.heightPixels / 2, 2) + Math.pow(fovDetails.completeScreenDistancePixels, 2));
|
||||||
const monitorSpacingPixels = monitorSpacing * fovDetails.heightPixels;
|
const monitorSpacingPixels = monitorSpacing * fovDetails.heightPixels;
|
||||||
|
|
||||||
cachedMonitorRadians[0] = -fovDetails.verticalRadians / 2;
|
cachedMonitorRadians[0] = -fovDetails.defaultDistanceVerticalRadians / 2;
|
||||||
monitorDetailsList.forEach(monitorDetails => {
|
monitorDetailsList.forEach(monitorDetails => {
|
||||||
const monitorWrapDetails = monitorWrap(cachedMonitorRadians, topEdgeRadius, monitorSpacingPixels, monitorDetails.y, monitorDetails.height);
|
const monitorWrapDetails = monitorWrap(cachedMonitorRadians, topEdgeRadius, monitorSpacingPixels, monitorDetails.y, monitorDetails.height);
|
||||||
|
const monitorCenterRadius = Math.sqrt(Math.pow(topEdgeRadius, 2) - Math.pow(monitorDetails.height / 2, 2));
|
||||||
const westPixels = monitorDetails.x + (monitorDetails.x / fovDetails.widthPixels) * monitorSpacingPixels;
|
const westPixels = monitorDetails.x + (monitorDetails.x / fovDetails.widthPixels) * monitorSpacingPixels;
|
||||||
const westCenterPixels = westPixels + monitorDetails.width / 2 - fovDetails.widthPixels / 2;
|
const westCenterPixels = westPixels + monitorDetails.width / 2 - fovDetails.widthPixels / 2;
|
||||||
|
|
||||||
monitorPlacements.push({
|
monitorPlacements.push({
|
||||||
topLeftNoRotate: [
|
topLeftNoRotate: [
|
||||||
fovDetails.completeScreenDistance,
|
monitorCenterRadius,
|
||||||
|
|
||||||
// west is flat when wrapping vertically, apply it here as a constant, not touched by rendering
|
// west is flat when wrapping vertically, apply it here as a constant, not touched by rendering
|
||||||
westPixels,
|
westPixels,
|
||||||
|
|
@ -263,7 +262,7 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorWrappingSch
|
||||||
(monitorDetails.height - fovDetails.heightPixels) / 2
|
(monitorDetails.height - fovDetails.heightPixels) / 2
|
||||||
],
|
],
|
||||||
centerNoRotate: [
|
centerNoRotate: [
|
||||||
fovDetails.completeScreenDistance,
|
monitorCenterRadius,
|
||||||
|
|
||||||
// west is flat when wrapping horizontally
|
// west is flat when wrapping horizontally
|
||||||
westCenterPixels,
|
westCenterPixels,
|
||||||
|
|
@ -273,13 +272,13 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorWrappingSch
|
||||||
],
|
],
|
||||||
center: [
|
center: [
|
||||||
// north is adjacent where radius is the hypotenuse, using monitorWrapDetails.center as the radians
|
// north is adjacent where radius is the hypotenuse, using monitorWrapDetails.center as the radians
|
||||||
fovDetails.completeScreenDistance * Math.cos(monitorWrapDetails.center),
|
monitorCenterRadius * Math.cos(monitorWrapDetails.center),
|
||||||
|
|
||||||
// west is flat when wrapping vertically
|
// west is flat when wrapping vertically
|
||||||
-westCenterPixels,
|
-westCenterPixels,
|
||||||
|
|
||||||
// up is opposite where radius is the hypotenuse, using monitorWrapDetails.center as the radians
|
// up is opposite where radius is the hypotenuse, using monitorWrapDetails.center as the radians
|
||||||
-fovDetails.completeScreenDistance * Math.sin(monitorWrapDetails.center)
|
-monitorCenterRadius * Math.sin(monitorWrapDetails.center)
|
||||||
],
|
],
|
||||||
rotationAngleRadians: {
|
rotationAngleRadians: {
|
||||||
x: -monitorWrapDetails.center,
|
x: -monitorWrapDetails.center,
|
||||||
|
|
@ -298,17 +297,17 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorWrappingSch
|
||||||
const upCenterPixels = upPixels + monitorDetails.height / 2 - fovDetails.heightPixels / 2;
|
const upCenterPixels = upPixels + monitorDetails.height / 2 - fovDetails.heightPixels / 2;
|
||||||
monitorPlacements.push({
|
monitorPlacements.push({
|
||||||
topLeftNoRotate: [
|
topLeftNoRotate: [
|
||||||
fovDetails.completeScreenDistance,
|
fovDetails.completeScreenDistancePixels,
|
||||||
westPixels,
|
westPixels,
|
||||||
-upPixels
|
-upPixels
|
||||||
],
|
],
|
||||||
centerNoRotate: [
|
centerNoRotate: [
|
||||||
fovDetails.completeScreenDistance,
|
fovDetails.completeScreenDistancePixels,
|
||||||
westCenterPixels,
|
westCenterPixels,
|
||||||
-upCenterPixels
|
-upCenterPixels
|
||||||
],
|
],
|
||||||
center: [
|
center: [
|
||||||
fovDetails.completeScreenDistance,
|
fovDetails.completeScreenDistancePixels,
|
||||||
-westCenterPixels,
|
-westCenterPixels,
|
||||||
-upCenterPixels
|
-upCenterPixels
|
||||||
],
|
],
|
||||||
|
|
@ -690,24 +689,25 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
||||||
|
|
||||||
_fov_details() {
|
_fov_details() {
|
||||||
const aspect = this.target_monitor.width / this.target_monitor.height;
|
const aspect = this.target_monitor.width / this.target_monitor.height;
|
||||||
const fovVerticalRadiansFullScreen = degreesToRadians(Globals.data_stream.device_data.displayFov / Math.sqrt(1 + aspect * aspect));
|
const fovRadians = diagonalToCrossFOVs(degreeToRadian(Globals.data_stream.device_data.displayFov), aspect);
|
||||||
const fovVerticalRadians = Math.atan(Math.tan(fovVerticalRadiansFullScreen) / this._display_distance_default());
|
const defaultDistanceVerticalRadians = 2 * Math.atan(Math.tan(fovRadians.vertical / 2) / this._display_distance_default());
|
||||||
|
const defaultDistanceHorizontalRadians = 2 * Math.atan(Math.tan(fovRadians.horizontal / 2) / this._display_distance_default());
|
||||||
|
|
||||||
// distance needed for the FOV-sized monitor to fill up the screen
|
// distance needed for the FOV-sized monitor to fill up the screen
|
||||||
const fullScreenDistance = this.target_monitor.height / 2 / Math.tan(fovVerticalRadiansFullScreen / 2);
|
const fullScreenDistance = this.target_monitor.height / 2 / Math.tan(fovRadians.vertical / 2);
|
||||||
const lensDistance = fullScreenDistance / (1.0 - Globals.data_stream.device_data.lensDistanceRatio) - fullScreenDistance;
|
const lensDistancePixels = fullScreenDistance / (1.0 - Globals.data_stream.device_data.lensDistanceRatio) - fullScreenDistance;
|
||||||
|
|
||||||
// distance of a display at the default (most zoomed out) distance, plus the lens distance constant
|
// distance of a display at the default (most zoomed out) distance, plus the lens distance constant
|
||||||
const lensToScreenDistance = this.target_monitor.height / 2 / Math.tan(fovVerticalRadians / 2);
|
const lensToScreenDistance = this.target_monitor.height / 2 / Math.tan(defaultDistanceVerticalRadians / 2);
|
||||||
const completeScreenDistance = lensToScreenDistance + lensDistance;
|
const completeScreenDistancePixels = lensToScreenDistance + lensDistancePixels;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
widthPixels: this.target_monitor.width,
|
widthPixels: this.target_monitor.width,
|
||||||
heightPixels: this.target_monitor.height,
|
heightPixels: this.target_monitor.height,
|
||||||
verticalRadians: fovVerticalRadians,
|
defaultDistanceVerticalRadians,
|
||||||
horizontalRadians: fovVerticalRadians * aspect,
|
defaultDistanceHorizontalRadians,
|
||||||
lensDistance,
|
lensDistancePixels,
|
||||||
completeScreenDistance
|
completeScreenDistancePixels
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -756,7 +756,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
||||||
this._horizontal_monitor_sort();
|
this._horizontal_monitor_sort();
|
||||||
|
|
||||||
const fovDetails = this._fov_details();
|
const fovDetails = this._fov_details();
|
||||||
this.lens_vector = [0.0, 0.0, -fovDetails.lensDistance];
|
this.lens_vector = [0.0, 0.0, -fovDetails.lensDistancePixels];
|
||||||
this.monitor_placements = monitorsToPlacements(
|
this.monitor_placements = monitorsToPlacements(
|
||||||
fovDetails,
|
fovDetails,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 748ce7816c50af50780b9186e394eaf3ebd3cb40
|
Subproject commit a274fc23c385b3f039fe6baba0a138fe31c7ad35
|
||||||
Loading…
Reference in New Issue