Fix display placement issues
This commit is contained in:
parent
f980d0bf2b
commit
a0527a0944
|
|
@ -1,3 +1,20 @@
|
|||
export function degreeToRadian(degree) {
|
||||
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 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
|
||||
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]);
|
||||
}
|
||||
|
||||
perspective(fovVerticalRadians, aspect, near, far) {
|
||||
const fovHorizontalRadians = fovVerticalRadians * aspect;
|
||||
perspective(fovHorizontalRadians, aspect, near, far) {
|
||||
const f = 1.0 / Math.tan(fovHorizontalRadians / 2.0);
|
||||
const range = far - near;
|
||||
|
||||
|
|
@ -383,17 +383,15 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
|||
vfunc_paint_target(node, paintContext) {
|
||||
if (!this._initialized) {
|
||||
const aspect = this.target_monitor.width / this.target_monitor.height;
|
||||
const fovDiagonalRadians = Globals.data_stream.device_data.displayFov * Math.PI / 180.0;
|
||||
const diagToVertRatio = Math.sqrt(aspect * aspect + 1);
|
||||
const fovVerticalRadians = fovDiagonalRadians / diagToVertRatio;
|
||||
const fovRadians = diagonalToCrossFOVs(degreeToRadian(Globals.data_stream.device_data.displayFov), aspect);
|
||||
const projection_matrix = this.perspective(
|
||||
fovVerticalRadians,
|
||||
fovRadians.horizontal,
|
||||
aspect,
|
||||
0.0001,
|
||||
1000.0
|
||||
);
|
||||
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_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);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import Shell from 'gi://Shell';
|
|||
import St from 'gi://St';
|
||||
|
||||
import { VirtualDisplayEffect } from './virtualdisplayeffect.js';
|
||||
import { degreeToRadian, diagonalToCrossFOVs } from './math.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 distanceToMonitorSize = distancePixels / monitor.width;
|
||||
const distanceFromCenterPixels = fovDetails.completeScreenDistancePixels * Math.tan(distance);
|
||||
const distanceFromCenterSizeRatio = distanceFromCenterPixels / monitor.width;
|
||||
|
||||
if (currentFocusedIndex === index) {
|
||||
currentFocusedDistance = distanceToMonitorSize * focusedMonitorDistance;
|
||||
currentFocusedDistance = distanceFromCenterSizeRatio * focusedMonitorDistance;
|
||||
}
|
||||
|
||||
if (distanceToMonitorSize < closestDistance) {
|
||||
if (distanceFromCenterSizeRatio < closestDistance) {
|
||||
closestIndex = index;
|
||||
closestDistance = distanceToMonitorSize;
|
||||
closestDistance = distanceFromCenterSizeRatio;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -108,10 +109,6 @@ function findFocusedMonitor(quaternion, monitorVectors, currentFocusedIndex, foc
|
|||
return currentFocusedIndex;
|
||||
}
|
||||
|
||||
function degreesToRadians(degrees) {
|
||||
return degrees * Math.PI / 180.0;
|
||||
}
|
||||
|
||||
/***
|
||||
* @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
|
||||
|
||||
// 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;
|
||||
|
||||
cachedMonitorRadians[0] = -fovDetails.horizontalRadians / 2;
|
||||
cachedMonitorRadians[0] = -fovDetails.defaultDistanceHorizontalRadians / 2;
|
||||
monitorDetailsList.forEach(monitorDetails => {
|
||||
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 upCenterPixels = upTopPixels + monitorDetails.height / 2 - fovDetails.heightPixels / 2;
|
||||
|
||||
monitorPlacements.push({
|
||||
topLeftNoRotate: [
|
||||
fovDetails.completeScreenDistance,
|
||||
monitorCenterRadius,
|
||||
|
||||
// west stays aligned with (0, 0), will apply rotationAngleRadians value during rendering
|
||||
-(monitorDetails.width - fovDetails.widthPixels) / 2,
|
||||
|
|
@ -215,7 +213,7 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorWrappingSch
|
|||
-upTopPixels
|
||||
],
|
||||
centerNoRotate: [
|
||||
fovDetails.completeScreenDistance,
|
||||
monitorCenterRadius,
|
||||
|
||||
// west centered about the FOV center
|
||||
0,
|
||||
|
|
@ -225,10 +223,10 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorWrappingSch
|
|||
],
|
||||
center: [
|
||||
// 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
|
||||
-fovDetails.completeScreenDistance * Math.sin(monitorWrapDetails.center),
|
||||
-monitorCenterRadius * Math.sin(monitorWrapDetails.center),
|
||||
|
||||
// up is flat when wrapping horizontally
|
||||
-upCenterPixels
|
||||
|
|
@ -243,18 +241,19 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorWrappingSch
|
|||
// 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
|
||||
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;
|
||||
|
||||
cachedMonitorRadians[0] = -fovDetails.verticalRadians / 2;
|
||||
cachedMonitorRadians[0] = -fovDetails.defaultDistanceVerticalRadians / 2;
|
||||
monitorDetailsList.forEach(monitorDetails => {
|
||||
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 westCenterPixels = westPixels + monitorDetails.width / 2 - fovDetails.widthPixels / 2;
|
||||
|
||||
monitorPlacements.push({
|
||||
topLeftNoRotate: [
|
||||
fovDetails.completeScreenDistance,
|
||||
monitorCenterRadius,
|
||||
|
||||
// west is flat when wrapping vertically, apply it here as a constant, not touched by rendering
|
||||
westPixels,
|
||||
|
|
@ -263,7 +262,7 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorWrappingSch
|
|||
(monitorDetails.height - fovDetails.heightPixels) / 2
|
||||
],
|
||||
centerNoRotate: [
|
||||
fovDetails.completeScreenDistance,
|
||||
monitorCenterRadius,
|
||||
|
||||
// west is flat when wrapping horizontally
|
||||
westCenterPixels,
|
||||
|
|
@ -273,13 +272,13 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorWrappingSch
|
|||
],
|
||||
center: [
|
||||
// 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
|
||||
-westCenterPixels,
|
||||
|
||||
// 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: {
|
||||
x: -monitorWrapDetails.center,
|
||||
|
|
@ -298,17 +297,17 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorWrappingSch
|
|||
const upCenterPixels = upPixels + monitorDetails.height / 2 - fovDetails.heightPixels / 2;
|
||||
monitorPlacements.push({
|
||||
topLeftNoRotate: [
|
||||
fovDetails.completeScreenDistance,
|
||||
fovDetails.completeScreenDistancePixels,
|
||||
westPixels,
|
||||
-upPixels
|
||||
],
|
||||
centerNoRotate: [
|
||||
fovDetails.completeScreenDistance,
|
||||
fovDetails.completeScreenDistancePixels,
|
||||
westCenterPixels,
|
||||
-upCenterPixels
|
||||
],
|
||||
center: [
|
||||
fovDetails.completeScreenDistance,
|
||||
fovDetails.completeScreenDistancePixels,
|
||||
-westCenterPixels,
|
||||
-upCenterPixels
|
||||
],
|
||||
|
|
@ -690,24 +689,25 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
|||
|
||||
_fov_details() {
|
||||
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 fovVerticalRadians = Math.atan(Math.tan(fovVerticalRadiansFullScreen) / this._display_distance_default());
|
||||
const fovRadians = diagonalToCrossFOVs(degreeToRadian(Globals.data_stream.device_data.displayFov), aspect);
|
||||
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
|
||||
const fullScreenDistance = this.target_monitor.height / 2 / Math.tan(fovVerticalRadiansFullScreen / 2);
|
||||
const lensDistance = fullScreenDistance / (1.0 - Globals.data_stream.device_data.lensDistanceRatio) - fullScreenDistance;
|
||||
const fullScreenDistance = this.target_monitor.height / 2 / Math.tan(fovRadians.vertical / 2);
|
||||
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
|
||||
const lensToScreenDistance = this.target_monitor.height / 2 / Math.tan(fovVerticalRadians / 2);
|
||||
const completeScreenDistance = lensToScreenDistance + lensDistance;
|
||||
const lensToScreenDistance = this.target_monitor.height / 2 / Math.tan(defaultDistanceVerticalRadians / 2);
|
||||
const completeScreenDistancePixels = lensToScreenDistance + lensDistancePixels;
|
||||
|
||||
return {
|
||||
widthPixels: this.target_monitor.width,
|
||||
heightPixels: this.target_monitor.height,
|
||||
verticalRadians: fovVerticalRadians,
|
||||
horizontalRadians: fovVerticalRadians * aspect,
|
||||
lensDistance,
|
||||
completeScreenDistance
|
||||
defaultDistanceVerticalRadians,
|
||||
defaultDistanceHorizontalRadians,
|
||||
lensDistancePixels,
|
||||
completeScreenDistancePixels
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -756,7 +756,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
|||
this._horizontal_monitor_sort();
|
||||
|
||||
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(
|
||||
fovDetails,
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 748ce7816c50af50780b9186e394eaf3ebd3cb40
|
||||
Subproject commit a274fc23c385b3f039fe6baba0a138fe31c7ad35
|
||||
Loading…
Reference in New Issue