@epicgames-ps/lib-pixelstreamingfrontend-ue5.5 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commonjs/AFK/AFKController.js +109 -109
- package/dist/commonjs/Config/Config.js +559 -559
- package/dist/commonjs/Config/SettingBase.js +98 -98
- package/dist/commonjs/Config/SettingFlag.js +49 -49
- package/dist/commonjs/Config/SettingNumber.js +83 -83
- package/dist/commonjs/Config/SettingOption.js +84 -84
- package/dist/commonjs/Config/SettingText.js +42 -42
- package/dist/commonjs/DataChannel/DataChannelController.js +106 -106
- package/dist/commonjs/DataChannel/DataChannelLatencyTestController.js +94 -94
- package/dist/commonjs/DataChannel/DataChannelLatencyTestResults.js +18 -18
- package/dist/commonjs/DataChannel/DataChannelSender.js +43 -43
- package/dist/commonjs/DataChannel/InitialSettings.js +41 -41
- package/dist/commonjs/DataChannel/LatencyTestResults.js +60 -60
- package/dist/commonjs/FreezeFrame/FreezeFrame.js +93 -93
- package/dist/commonjs/FreezeFrame/FreezeFrameController.js +95 -95
- package/dist/commonjs/Inputs/GamepadController.js +188 -188
- package/dist/commonjs/Inputs/GamepadTypes.js +21 -21
- package/dist/commonjs/Inputs/IInputController.js +2 -2
- package/dist/commonjs/Inputs/InputClassesFactory.js +96 -96
- package/dist/commonjs/Inputs/KeyCodes.js +112 -112
- package/dist/commonjs/Inputs/KeyboardController.js +137 -137
- package/dist/commonjs/Inputs/MouseButtons.js +28 -28
- package/dist/commonjs/Inputs/MouseController.js +97 -97
- package/dist/commonjs/Inputs/MouseControllerHovering.js +93 -93
- package/dist/commonjs/Inputs/MouseControllerLocked.js +153 -153
- package/dist/commonjs/Inputs/SpecialKeyCodes.js +19 -19
- package/dist/commonjs/Inputs/TouchController.js +123 -123
- package/dist/commonjs/Inputs/TouchControllerFake.js +91 -91
- package/dist/commonjs/Inputs/XRGamepadController.js +124 -124
- package/dist/commonjs/PeerConnectionController/AggregatedStats.js +252 -252
- package/dist/commonjs/PeerConnectionController/CandidatePairStats.js +10 -10
- package/dist/commonjs/PeerConnectionController/CandidateStat.js +10 -10
- package/dist/commonjs/PeerConnectionController/CodecStats.js +10 -10
- package/dist/commonjs/PeerConnectionController/DataChannelStats.js +10 -10
- package/dist/commonjs/PeerConnectionController/InboundRTPStats.js +22 -22
- package/dist/commonjs/PeerConnectionController/InboundTrackStats.js +10 -10
- package/dist/commonjs/PeerConnectionController/OutBoundRTPStats.js +16 -16
- package/dist/commonjs/PeerConnectionController/PeerConnectionController.js +584 -584
- package/dist/commonjs/PeerConnectionController/SessionStats.js +10 -10
- package/dist/commonjs/PeerConnectionController/StreamStats.js +10 -10
- package/dist/commonjs/PixelStreaming/PixelStreaming.js +607 -607
- package/dist/commonjs/UI/OnScreenKeyboard.js +82 -82
- package/dist/commonjs/UeInstanceMessage/ResponseController.js +38 -38
- package/dist/commonjs/UeInstanceMessage/SendMessageController.js +120 -120
- package/dist/commonjs/UeInstanceMessage/StreamMessageController.js +210 -210
- package/dist/commonjs/UeInstanceMessage/ToStreamerMessagesController.js +49 -49
- package/dist/commonjs/Util/EventEmitter.js +386 -386
- package/dist/commonjs/Util/FileUtil.js +108 -108
- package/dist/commonjs/Util/IURLSearchParams.js +25 -25
- package/dist/commonjs/Util/InputCoordTranslator.js +49 -49
- package/dist/commonjs/Util/RTCUtils.js +40 -40
- package/dist/commonjs/VideoPlayer/StreamController.js +67 -67
- package/dist/commonjs/VideoPlayer/VideoPlayer.js +177 -177
- package/dist/commonjs/WebRtcPlayer/WebRtcPlayerController.js +1221 -1221
- package/dist/commonjs/WebXR/WebXRController.js +335 -335
- package/dist/commonjs/pixelstreamingfrontend.js +70 -70
- package/dist/esm/AFK/AFKController.js +105 -105
- package/dist/esm/Config/Config.js +551 -551
- package/dist/esm/Config/SettingBase.js +94 -94
- package/dist/esm/Config/SettingFlag.js +45 -45
- package/dist/esm/Config/SettingNumber.js +79 -79
- package/dist/esm/Config/SettingOption.js +80 -80
- package/dist/esm/Config/SettingText.js +38 -38
- package/dist/esm/DataChannel/DataChannelController.js +102 -102
- package/dist/esm/DataChannel/DataChannelLatencyTestController.js +90 -90
- package/dist/esm/DataChannel/DataChannelLatencyTestResults.js +14 -14
- package/dist/esm/DataChannel/DataChannelSender.js +39 -39
- package/dist/esm/DataChannel/InitialSettings.js +34 -34
- package/dist/esm/DataChannel/LatencyTestResults.js +56 -56
- package/dist/esm/FreezeFrame/FreezeFrame.js +89 -89
- package/dist/esm/FreezeFrame/FreezeFrameController.js +91 -91
- package/dist/esm/Inputs/GamepadController.js +184 -184
- package/dist/esm/Inputs/GamepadTypes.js +17 -17
- package/dist/esm/Inputs/IInputController.js +1 -1
- package/dist/esm/Inputs/InputClassesFactory.js +91 -91
- package/dist/esm/Inputs/KeyCodes.js +109 -109
- package/dist/esm/Inputs/KeyboardController.js +133 -133
- package/dist/esm/Inputs/MouseButtons.js +23 -23
- package/dist/esm/Inputs/MouseController.js +93 -93
- package/dist/esm/Inputs/MouseControllerHovering.js +89 -89
- package/dist/esm/Inputs/MouseControllerLocked.js +149 -149
- package/dist/esm/Inputs/SpecialKeyCodes.js +15 -15
- package/dist/esm/Inputs/TouchController.js +119 -119
- package/dist/esm/Inputs/TouchControllerFake.js +87 -87
- package/dist/esm/Inputs/XRGamepadController.js +120 -120
- package/dist/esm/PeerConnectionController/AggregatedStats.js +248 -248
- package/dist/esm/PeerConnectionController/CandidatePairStats.js +6 -6
- package/dist/esm/PeerConnectionController/CandidateStat.js +6 -6
- package/dist/esm/PeerConnectionController/CodecStats.js +6 -6
- package/dist/esm/PeerConnectionController/DataChannelStats.js +6 -6
- package/dist/esm/PeerConnectionController/InboundRTPStats.js +16 -16
- package/dist/esm/PeerConnectionController/InboundTrackStats.js +6 -6
- package/dist/esm/PeerConnectionController/OutBoundRTPStats.js +11 -11
- package/dist/esm/PeerConnectionController/PeerConnectionController.js +580 -580
- package/dist/esm/PeerConnectionController/SessionStats.js +6 -6
- package/dist/esm/PeerConnectionController/StreamStats.js +6 -6
- package/dist/esm/PixelStreaming/PixelStreaming.js +603 -603
- package/dist/esm/UI/OnScreenKeyboard.js +78 -78
- package/dist/esm/UeInstanceMessage/ResponseController.js +34 -34
- package/dist/esm/UeInstanceMessage/SendMessageController.js +116 -116
- package/dist/esm/UeInstanceMessage/StreamMessageController.js +205 -205
- package/dist/esm/UeInstanceMessage/ToStreamerMessagesController.js +45 -45
- package/dist/esm/Util/EventEmitter.js +345 -345
- package/dist/esm/Util/FileUtil.js +103 -103
- package/dist/esm/Util/IURLSearchParams.js +21 -21
- package/dist/esm/Util/InputCoordTranslator.js +45 -45
- package/dist/esm/Util/RTCUtils.js +36 -36
- package/dist/esm/VideoPlayer/StreamController.js +63 -63
- package/dist/esm/VideoPlayer/VideoPlayer.js +173 -173
- package/dist/esm/WebRtcPlayer/WebRtcPlayerController.js +1217 -1217
- package/dist/esm/WebXR/WebXRController.js +331 -331
- package/dist/esm/pixelstreamingfrontend.js +24 -24
- package/dist/types/AFK/AFKController.d.ts +38 -38
- package/dist/types/Config/Config.d.ts +220 -220
- package/dist/types/Config/SettingBase.d.ts +43 -43
- package/dist/types/Config/SettingFlag.d.ts +24 -24
- package/dist/types/Config/SettingNumber.d.ts +41 -41
- package/dist/types/Config/SettingOption.d.ts +41 -41
- package/dist/types/Config/SettingText.d.ts +21 -21
- package/dist/types/DataChannel/DataChannelController.d.ts +59 -59
- package/dist/types/DataChannel/DataChannelLatencyTestController.d.ts +25 -25
- package/dist/types/DataChannel/DataChannelLatencyTestResults.d.ts +46 -46
- package/dist/types/DataChannel/DataChannelSender.d.ts +21 -21
- package/dist/types/DataChannel/InitialSettings.d.ts +44 -44
- package/dist/types/DataChannel/LatencyTestResults.d.ts +31 -31
- package/dist/types/FreezeFrame/FreezeFrame.d.ts +36 -36
- package/dist/types/FreezeFrame/FreezeFrameController.d.ts +37 -37
- package/dist/types/Inputs/GamepadController.d.ts +61 -61
- package/dist/types/Inputs/GamepadTypes.d.ts +15 -15
- package/dist/types/Inputs/IInputController.d.ts +16 -16
- package/dist/types/Inputs/InputClassesFactory.d.ts +53 -53
- package/dist/types/Inputs/KeyCodes.d.ts +5 -5
- package/dist/types/Inputs/KeyboardController.d.ts +34 -34
- package/dist/types/Inputs/MouseButtons.d.ts +22 -22
- package/dist/types/Inputs/MouseController.d.ts +40 -40
- package/dist/types/Inputs/MouseControllerHovering.d.ts +26 -26
- package/dist/types/Inputs/MouseControllerLocked.d.ts +31 -31
- package/dist/types/Inputs/SpecialKeyCodes.d.ts +14 -14
- package/dist/types/Inputs/TouchController.d.ts +28 -28
- package/dist/types/Inputs/TouchControllerFake.d.ts +29 -29
- package/dist/types/Inputs/XRGamepadController.d.ts +15 -15
- package/dist/types/PeerConnectionController/AggregatedStats.d.ts +82 -82
- package/dist/types/PeerConnectionController/CandidatePairStats.d.ts +22 -22
- package/dist/types/PeerConnectionController/CandidateStat.d.ts +13 -13
- package/dist/types/PeerConnectionController/CodecStats.d.ts +14 -14
- package/dist/types/PeerConnectionController/DataChannelStats.d.ts +15 -15
- package/dist/types/PeerConnectionController/InboundRTPStats.d.ts +141 -141
- package/dist/types/PeerConnectionController/InboundTrackStats.d.ts +32 -32
- package/dist/types/PeerConnectionController/OutBoundRTPStats.d.ts +23 -23
- package/dist/types/PeerConnectionController/PeerConnectionController.d.ts +143 -143
- package/dist/types/PeerConnectionController/SessionStats.d.ts +8 -8
- package/dist/types/PeerConnectionController/StreamStats.d.ts +9 -9
- package/dist/types/PixelStreaming/PixelStreaming.d.ts +271 -271
- package/dist/types/UI/OnScreenKeyboard.d.ts +30 -30
- package/dist/types/UeInstanceMessage/ResponseController.d.ts +19 -19
- package/dist/types/UeInstanceMessage/SendMessageController.d.ts +18 -18
- package/dist/types/UeInstanceMessage/StreamMessageController.d.ts +29 -29
- package/dist/types/UeInstanceMessage/ToStreamerMessagesController.d.ts +32 -32
- package/dist/types/Util/EventEmitter.d.ts +429 -429
- package/dist/types/Util/FileUtil.d.ts +32 -32
- package/dist/types/Util/IURLSearchParams.d.ts +9 -9
- package/dist/types/Util/InputCoordTranslator.d.ts +29 -29
- package/dist/types/Util/RTCUtils.d.ts +8 -8
- package/dist/types/VideoPlayer/StreamController.d.ts +22 -22
- package/dist/types/VideoPlayer/VideoPlayer.d.ts +78 -78
- package/dist/types/WebRtcPlayer/WebRtcPlayerController.d.ts +379 -379
- package/dist/types/WebXR/WebXRController.d.ts +42 -42
- package/dist/types/pixelstreamingfrontend.d.ts +23 -23
- package/package.json +45 -45
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.WebXRController = void 0;
|
|
5
|
-
const lib_pixelstreamingcommon_ue5_5_1 = require("@epicgames-ps/lib-pixelstreamingcommon-ue5.5");
|
|
6
|
-
const XRGamepadController_1 = require("../Inputs/XRGamepadController");
|
|
7
|
-
const EventEmitter_1 = require("../Util/EventEmitter");
|
|
8
|
-
const pixelstreamingfrontend_1 = require("../pixelstreamingfrontend");
|
|
9
|
-
class WebXRController {
|
|
10
|
-
constructor(webRtcPlayerController) {
|
|
11
|
-
this.xrViewerPose = null;
|
|
12
|
-
// Used for comparisons to ensure two numbers are close enough.
|
|
13
|
-
this.EPSILON = 0.0000001;
|
|
14
|
-
this.videoTexture = null;
|
|
15
|
-
this.prevVideoWidth = 0;
|
|
16
|
-
this.prevVideoHeight = 0;
|
|
17
|
-
this.leftView = null;
|
|
18
|
-
this.rightView = null;
|
|
19
|
-
// Store the HMD data we have last sent (not all of it is needed every frame unless it changes)
|
|
20
|
-
this.lastSentLeftEyeProj = null;
|
|
21
|
-
this.lastSentRightEyeProj = null;
|
|
22
|
-
this.lastSentRelativeLeftEyePos = null;
|
|
23
|
-
this.lastSentRelativeRightEyePos = null;
|
|
24
|
-
this.xrSession = null;
|
|
25
|
-
this.webRtcController = webRtcPlayerController;
|
|
26
|
-
this.xrGamepadController = new XRGamepadController_1.XRGamepadController(this.webRtcController.streamMessageController);
|
|
27
|
-
this.onSessionEnded = new EventTarget();
|
|
28
|
-
this.onSessionStarted = new EventTarget();
|
|
29
|
-
this.onFrame = new EventTarget();
|
|
30
|
-
}
|
|
31
|
-
xrClicked() {
|
|
32
|
-
if (!this.xrSession) {
|
|
33
|
-
if (!navigator.xr) {
|
|
34
|
-
lib_pixelstreamingcommon_ue5_5_1.Logger.Error('This browser does not support XR.');
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
navigator.xr
|
|
38
|
-
/* Request immersive-vr session without any optional features. */
|
|
39
|
-
.requestSession('immersive-vr', { optionalFeatures: [] })
|
|
40
|
-
.then((session) => {
|
|
41
|
-
this.onXrSessionStarted(session);
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
this.xrSession.end();
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
onXrSessionEnded() {
|
|
49
|
-
lib_pixelstreamingcommon_ue5_5_1.Logger.Info('XR Session ended');
|
|
50
|
-
this.xrSession = null;
|
|
51
|
-
this.onSessionEnded.dispatchEvent(new Event('xrSessionEnded'));
|
|
52
|
-
}
|
|
53
|
-
initGL() {
|
|
54
|
-
if (this.gl) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
const canvas = document.createElement('canvas');
|
|
58
|
-
this.gl = canvas.getContext('webgl2', {
|
|
59
|
-
xrCompatible: true
|
|
60
|
-
});
|
|
61
|
-
// Set our clear color
|
|
62
|
-
this.gl.clearColor(0.0, 0.0, 0.0, 1);
|
|
63
|
-
}
|
|
64
|
-
initShaders() {
|
|
65
|
-
// shader source code
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.WebXRController = void 0;
|
|
5
|
+
const lib_pixelstreamingcommon_ue5_5_1 = require("@epicgames-ps/lib-pixelstreamingcommon-ue5.5");
|
|
6
|
+
const XRGamepadController_1 = require("../Inputs/XRGamepadController");
|
|
7
|
+
const EventEmitter_1 = require("../Util/EventEmitter");
|
|
8
|
+
const pixelstreamingfrontend_1 = require("../pixelstreamingfrontend");
|
|
9
|
+
class WebXRController {
|
|
10
|
+
constructor(webRtcPlayerController) {
|
|
11
|
+
this.xrViewerPose = null;
|
|
12
|
+
// Used for comparisons to ensure two numbers are close enough.
|
|
13
|
+
this.EPSILON = 0.0000001;
|
|
14
|
+
this.videoTexture = null;
|
|
15
|
+
this.prevVideoWidth = 0;
|
|
16
|
+
this.prevVideoHeight = 0;
|
|
17
|
+
this.leftView = null;
|
|
18
|
+
this.rightView = null;
|
|
19
|
+
// Store the HMD data we have last sent (not all of it is needed every frame unless it changes)
|
|
20
|
+
this.lastSentLeftEyeProj = null;
|
|
21
|
+
this.lastSentRightEyeProj = null;
|
|
22
|
+
this.lastSentRelativeLeftEyePos = null;
|
|
23
|
+
this.lastSentRelativeRightEyePos = null;
|
|
24
|
+
this.xrSession = null;
|
|
25
|
+
this.webRtcController = webRtcPlayerController;
|
|
26
|
+
this.xrGamepadController = new XRGamepadController_1.XRGamepadController(this.webRtcController.streamMessageController);
|
|
27
|
+
this.onSessionEnded = new EventTarget();
|
|
28
|
+
this.onSessionStarted = new EventTarget();
|
|
29
|
+
this.onFrame = new EventTarget();
|
|
30
|
+
}
|
|
31
|
+
xrClicked() {
|
|
32
|
+
if (!this.xrSession) {
|
|
33
|
+
if (!navigator.xr) {
|
|
34
|
+
lib_pixelstreamingcommon_ue5_5_1.Logger.Error('This browser does not support XR.');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
navigator.xr
|
|
38
|
+
/* Request immersive-vr session without any optional features. */
|
|
39
|
+
.requestSession('immersive-vr', { optionalFeatures: [] })
|
|
40
|
+
.then((session) => {
|
|
41
|
+
this.onXrSessionStarted(session);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
this.xrSession.end();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
onXrSessionEnded() {
|
|
49
|
+
lib_pixelstreamingcommon_ue5_5_1.Logger.Info('XR Session ended');
|
|
50
|
+
this.xrSession = null;
|
|
51
|
+
this.onSessionEnded.dispatchEvent(new Event('xrSessionEnded'));
|
|
52
|
+
}
|
|
53
|
+
initGL() {
|
|
54
|
+
if (this.gl) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const canvas = document.createElement('canvas');
|
|
58
|
+
this.gl = canvas.getContext('webgl2', {
|
|
59
|
+
xrCompatible: true
|
|
60
|
+
});
|
|
61
|
+
// Set our clear color
|
|
62
|
+
this.gl.clearColor(0.0, 0.0, 0.0, 1);
|
|
63
|
+
}
|
|
64
|
+
initShaders() {
|
|
65
|
+
// shader source code
|
|
66
66
|
const vertexShaderSource = `
|
|
67
67
|
attribute vec2 a_position;
|
|
68
68
|
attribute vec2 a_texCoord;
|
|
@@ -76,7 +76,7 @@ class WebXRController {
|
|
|
76
76
|
// The GPU will interpolate this value between points.
|
|
77
77
|
v_texCoord = a_texCoord;
|
|
78
78
|
}
|
|
79
|
-
`;
|
|
79
|
+
`;
|
|
80
80
|
const fragmentShaderSource = `
|
|
81
81
|
precision mediump float;
|
|
82
82
|
|
|
@@ -89,273 +89,273 @@ class WebXRController {
|
|
|
89
89
|
void main() {
|
|
90
90
|
gl_FragColor = texture2D(u_image, v_texCoord);
|
|
91
91
|
}
|
|
92
|
-
`;
|
|
93
|
-
// setup vertex shader
|
|
94
|
-
const vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
|
|
95
|
-
this.gl.shaderSource(vertexShader, vertexShaderSource);
|
|
96
|
-
this.gl.compileShader(vertexShader);
|
|
97
|
-
// setup fragment shader
|
|
98
|
-
const fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
|
|
99
|
-
this.gl.shaderSource(fragmentShader, fragmentShaderSource);
|
|
100
|
-
this.gl.compileShader(fragmentShader);
|
|
101
|
-
// setup GLSL program
|
|
102
|
-
const shaderProgram = this.gl.createProgram();
|
|
103
|
-
this.gl.attachShader(shaderProgram, vertexShader);
|
|
104
|
-
this.gl.attachShader(shaderProgram, fragmentShader);
|
|
105
|
-
this.gl.linkProgram(shaderProgram);
|
|
106
|
-
this.gl.useProgram(shaderProgram);
|
|
107
|
-
// look up where vertex data needs to go
|
|
108
|
-
this.positionLocation = this.gl.getAttribLocation(shaderProgram, 'a_position');
|
|
109
|
-
this.texcoordLocation = this.gl.getAttribLocation(shaderProgram, 'a_texCoord');
|
|
110
|
-
}
|
|
111
|
-
updateVideoTexture() {
|
|
112
|
-
if (!this.videoTexture) {
|
|
113
|
-
// Create our texture that we use in our shader
|
|
114
|
-
// and bind it once because we never use any other texture.
|
|
115
|
-
this.videoTexture = this.gl.createTexture();
|
|
116
|
-
this.gl.bindTexture(this.gl.TEXTURE_2D, this.videoTexture);
|
|
117
|
-
// Set the parameters so we can render any size image.
|
|
118
|
-
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
|
|
119
|
-
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
|
|
120
|
-
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
|
|
121
|
-
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
|
|
122
|
-
}
|
|
123
|
-
const videoHeight = this.webRtcController.videoPlayer.getVideoElement().videoHeight;
|
|
124
|
-
const videoWidth = this.webRtcController.videoPlayer.getVideoElement().videoWidth;
|
|
125
|
-
if (this.prevVideoHeight != videoHeight || this.prevVideoWidth != videoWidth) {
|
|
126
|
-
// Do full update of texture if dimensions do not match
|
|
127
|
-
this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, videoWidth, videoHeight, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, this.webRtcController.videoPlayer.getVideoElement());
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
// If dimensions match just update the sub region
|
|
131
|
-
this.gl.texSubImage2D(this.gl.TEXTURE_2D, 0, 0, 0, videoWidth, videoHeight, this.gl.RGBA, this.gl.UNSIGNED_BYTE, this.webRtcController.videoPlayer.getVideoElement());
|
|
132
|
-
}
|
|
133
|
-
// Update prev video width/height
|
|
134
|
-
this.prevVideoHeight = videoHeight;
|
|
135
|
-
this.prevVideoWidth = videoWidth;
|
|
136
|
-
}
|
|
137
|
-
initBuffers() {
|
|
138
|
-
// Create out position buffer and its vertex shader attribute
|
|
139
|
-
{
|
|
140
|
-
// Create a buffer to put the the vertices of the plane we will draw the video stream onto
|
|
141
|
-
this.positionBuffer = this.gl.createBuffer();
|
|
142
|
-
// Bind the position buffer
|
|
143
|
-
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
|
|
144
|
-
// Enable `positionLocation` to be used as vertex shader attribute
|
|
145
|
-
this.gl.enableVertexAttribArray(this.positionLocation);
|
|
146
|
-
// Note: positions are passed in clip-space coordinates [-1..1] so no need to convert in-shader
|
|
147
|
-
// prettier-ignore
|
|
148
|
-
this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([
|
|
149
|
-
-1.0, 1.0,
|
|
150
|
-
1.0, 1.0,
|
|
151
|
-
-1.0, -1.0,
|
|
152
|
-
-1.0, -1.0,
|
|
153
|
-
1.0, 1.0,
|
|
154
|
-
1.0, -1.0
|
|
155
|
-
]), this.gl.STATIC_DRAW);
|
|
156
|
-
// Tell position attribute of the vertex shader how to get data out of the bound buffer (the positionBuffer)
|
|
157
|
-
this.gl.vertexAttribPointer(this.positionLocation, 2 /*size*/, this.gl.FLOAT /*type*/, false /*normalize*/, 0 /*stride*/, 0 /*offset*/);
|
|
158
|
-
}
|
|
159
|
-
// Create our texture coordinate buffers for accessing our texture
|
|
160
|
-
{
|
|
161
|
-
this.texcoordBuffer = this.gl.createBuffer();
|
|
162
|
-
// Bind the texture coordinate buffer
|
|
163
|
-
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texcoordBuffer);
|
|
164
|
-
// Enable `texcoordLocation` to be used as a vertex shader attribute
|
|
165
|
-
this.gl.enableVertexAttribArray(this.texcoordLocation);
|
|
166
|
-
// The texture coordinates to apply for rectangle we are drawing
|
|
167
|
-
this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0]), this.gl.STATIC_DRAW);
|
|
168
|
-
// Tell texture coordinate attribute of the vertex shader how to get data out of the bound buffer (the texcoordBuffer)
|
|
169
|
-
this.gl.vertexAttribPointer(this.texcoordLocation, 2 /*size*/, this.gl.FLOAT /*type*/, false /*normalize*/, 0 /*stride*/, 0 /*offset*/);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
onXrSessionStarted(session) {
|
|
173
|
-
lib_pixelstreamingcommon_ue5_5_1.Logger.Info('XR Session started');
|
|
174
|
-
this.xrSession = session;
|
|
175
|
-
this.xrSession.addEventListener('end', () => {
|
|
176
|
-
this.onXrSessionEnded();
|
|
177
|
-
});
|
|
178
|
-
// Initialization
|
|
179
|
-
this.initGL();
|
|
180
|
-
this.initShaders();
|
|
181
|
-
this.initBuffers();
|
|
182
|
-
session.requestReferenceSpace('local').then((refSpace) => {
|
|
183
|
-
this.xrRefSpace = refSpace;
|
|
184
|
-
// Set up our base layer (i.e. a projection layer that fills the entire XR viewport).
|
|
185
|
-
this.xrSession.updateRenderState({
|
|
186
|
-
baseLayer: new XRWebGLLayer(this.xrSession, this.gl)
|
|
187
|
-
});
|
|
188
|
-
// Update target framerate to 90 fps if 90 fps is supported in this XR device
|
|
189
|
-
if (this.xrSession.supportedFrameRates) {
|
|
190
|
-
for (const frameRate of this.xrSession.supportedFrameRates) {
|
|
191
|
-
if (frameRate == 90) {
|
|
192
|
-
session.updateTargetFrameRate(90);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
// Binding to each new frame to get latest XR updates
|
|
197
|
-
this.xrSession.requestAnimationFrame(this.onXrFrame.bind(this));
|
|
198
|
-
});
|
|
199
|
-
this.onSessionStarted.dispatchEvent(new Event('xrSessionStarted'));
|
|
200
|
-
}
|
|
201
|
-
areArraysEqual(a, b) {
|
|
202
|
-
return (a.length === b.length && a.every((element, index) => Math.abs(element - b[index]) <= this.EPSILON));
|
|
203
|
-
}
|
|
204
|
-
arePointsEqual(a, b) {
|
|
205
|
-
return (Math.abs(a.x - b.x) >= this.EPSILON &&
|
|
206
|
-
Math.abs(a.y - b.y) >= this.EPSILON &&
|
|
207
|
-
Math.abs(a.z - b.z) >= this.EPSILON);
|
|
208
|
-
}
|
|
209
|
-
sendXRDataToUE() {
|
|
210
|
-
if (this.leftView == null || this.rightView == null) {
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
// We selectively send either the `XREyeViews` or `XRHMDTransform`
|
|
214
|
-
// messages over the datachannel. The reason for this selective sending is that
|
|
215
|
-
// the `XREyeViews` is a much larger message and changes infrequently (e.g. only when user changes headset IPD).
|
|
216
|
-
// Therefore, we only need to send it once on startup and then any time it changes.
|
|
217
|
-
// The rest of the time we can send the `XRHMDTransform` message.
|
|
218
|
-
let shouldSendEyeViews = this.lastSentLeftEyeProj == null ||
|
|
219
|
-
this.lastSentRightEyeProj == null ||
|
|
220
|
-
this.lastSentRelativeLeftEyePos == null ||
|
|
221
|
-
this.lastSentRelativeRightEyePos == null;
|
|
222
|
-
const leftEyeTrans = this.leftView.transform.matrix;
|
|
223
|
-
const leftEyeProj = this.leftView.projectionMatrix;
|
|
224
|
-
const rightEyeTrans = this.rightView.transform.matrix;
|
|
225
|
-
const rightEyeProj = this.rightView.projectionMatrix;
|
|
226
|
-
const hmdTrans = this.xrViewerPose.transform.matrix;
|
|
227
|
-
// Check if projection matrices have changed
|
|
228
|
-
if (!shouldSendEyeViews && this.lastSentLeftEyeProj != null && this.lastSentRightEyeProj != null) {
|
|
229
|
-
const leftEyeProjUnchanged = this.areArraysEqual(leftEyeProj, this.lastSentLeftEyeProj);
|
|
230
|
-
const rightEyeProjUnchanged = this.areArraysEqual(rightEyeProj, this.lastSentRightEyeProj);
|
|
231
|
-
shouldSendEyeViews = leftEyeProjUnchanged == false || rightEyeProjUnchanged == false;
|
|
232
|
-
}
|
|
233
|
-
const leftEyeRelativePos = new DOMPointReadOnly(this.leftView.transform.position.x - this.xrViewerPose.transform.position.x, this.leftView.transform.position.y - this.xrViewerPose.transform.position.y, this.leftView.transform.position.z - this.xrViewerPose.transform.position.z, 1.0);
|
|
234
|
-
const rightEyeRelativePos = new DOMPointReadOnly(this.leftView.transform.position.x - this.xrViewerPose.transform.position.x, this.leftView.transform.position.y - this.xrViewerPose.transform.position.y, this.leftView.transform.position.z - this.xrViewerPose.transform.position.z, 1.0);
|
|
235
|
-
// Check if relative eye pos has changed (e.g IPD changed)
|
|
236
|
-
if (!shouldSendEyeViews &&
|
|
237
|
-
this.lastSentRelativeLeftEyePos != null &&
|
|
238
|
-
this.lastSentRelativeRightEyePos != null) {
|
|
239
|
-
const leftEyePosUnchanged = this.arePointsEqual(leftEyeRelativePos, this.lastSentRelativeLeftEyePos);
|
|
240
|
-
const rightEyePosUnchanged = this.arePointsEqual(rightEyeRelativePos, this.lastSentRelativeRightEyePos);
|
|
241
|
-
shouldSendEyeViews = leftEyePosUnchanged == false || rightEyePosUnchanged == false;
|
|
242
|
-
// Note: We are not checking if EyeView rotation changes (as far as I know no HMD supports changing this value at runtime).
|
|
243
|
-
}
|
|
244
|
-
if (shouldSendEyeViews) {
|
|
245
|
-
// send transform (4x4) and projection matrix (4x4) data for each eye (left first, then right)
|
|
246
|
-
// prettier-ignore
|
|
247
|
-
this.webRtcController.streamMessageController.toStreamerHandlers.get('XREyeViews')([
|
|
248
|
-
// Left eye 4x4 transform matrix
|
|
249
|
-
leftEyeTrans[0], leftEyeTrans[4], leftEyeTrans[8], leftEyeTrans[12],
|
|
250
|
-
leftEyeTrans[1], leftEyeTrans[5], leftEyeTrans[9], leftEyeTrans[13],
|
|
251
|
-
leftEyeTrans[2], leftEyeTrans[6], leftEyeTrans[10], leftEyeTrans[14],
|
|
252
|
-
leftEyeTrans[3], leftEyeTrans[7], leftEyeTrans[11], leftEyeTrans[15],
|
|
253
|
-
// Left eye 4x4 projection matrix
|
|
254
|
-
leftEyeProj[0], leftEyeProj[4], leftEyeProj[8], leftEyeProj[12],
|
|
255
|
-
leftEyeProj[1], leftEyeProj[5], leftEyeProj[9], leftEyeProj[13],
|
|
256
|
-
leftEyeProj[2], leftEyeProj[6], leftEyeProj[10], leftEyeProj[14],
|
|
257
|
-
leftEyeProj[3], leftEyeProj[7], leftEyeProj[11], leftEyeProj[15],
|
|
258
|
-
// Right eye 4x4 transform matrix
|
|
259
|
-
rightEyeTrans[0], rightEyeTrans[4], rightEyeTrans[8], rightEyeTrans[12],
|
|
260
|
-
rightEyeTrans[1], rightEyeTrans[5], rightEyeTrans[9], rightEyeTrans[13],
|
|
261
|
-
rightEyeTrans[2], rightEyeTrans[6], rightEyeTrans[10], rightEyeTrans[14],
|
|
262
|
-
rightEyeTrans[3], rightEyeTrans[7], rightEyeTrans[11], rightEyeTrans[15],
|
|
263
|
-
// right eye 4x4 projection matrix
|
|
264
|
-
rightEyeProj[0], rightEyeProj[4], rightEyeProj[8], rightEyeProj[12],
|
|
265
|
-
rightEyeProj[1], rightEyeProj[5], rightEyeProj[9], rightEyeProj[13],
|
|
266
|
-
rightEyeProj[2], rightEyeProj[6], rightEyeProj[10], rightEyeProj[14],
|
|
267
|
-
rightEyeProj[3], rightEyeProj[7], rightEyeProj[11], rightEyeProj[15],
|
|
268
|
-
// HMD 4x4 transform
|
|
269
|
-
hmdTrans[0], hmdTrans[4], hmdTrans[8], hmdTrans[12],
|
|
270
|
-
hmdTrans[1], hmdTrans[5], hmdTrans[9], hmdTrans[13],
|
|
271
|
-
hmdTrans[2], hmdTrans[6], hmdTrans[10], hmdTrans[14],
|
|
272
|
-
hmdTrans[3], hmdTrans[7], hmdTrans[11], hmdTrans[15],
|
|
273
|
-
]);
|
|
274
|
-
this.lastSentLeftEyeProj = leftEyeProj;
|
|
275
|
-
this.lastSentRightEyeProj = rightEyeProj;
|
|
276
|
-
this.lastSentRelativeLeftEyePos = leftEyeRelativePos;
|
|
277
|
-
this.lastSentRelativeRightEyePos = rightEyeRelativePos;
|
|
278
|
-
}
|
|
279
|
-
else {
|
|
280
|
-
// If we don't need to the entire eye views being sent just send the HMD transform
|
|
281
|
-
this.webRtcController.streamMessageController.toStreamerHandlers.get('XRHMDTransform')([
|
|
282
|
-
// HMD 4x4 transform
|
|
283
|
-
hmdTrans[0],
|
|
284
|
-
hmdTrans[4],
|
|
285
|
-
hmdTrans[8],
|
|
286
|
-
hmdTrans[12],
|
|
287
|
-
hmdTrans[1],
|
|
288
|
-
hmdTrans[5],
|
|
289
|
-
hmdTrans[9],
|
|
290
|
-
hmdTrans[13],
|
|
291
|
-
hmdTrans[2],
|
|
292
|
-
hmdTrans[6],
|
|
293
|
-
hmdTrans[10],
|
|
294
|
-
hmdTrans[14],
|
|
295
|
-
hmdTrans[3],
|
|
296
|
-
hmdTrans[7],
|
|
297
|
-
hmdTrans[11],
|
|
298
|
-
hmdTrans[15]
|
|
299
|
-
]);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
onXrFrame(time, frame) {
|
|
303
|
-
this.xrViewerPose = frame.getViewerPose(this.xrRefSpace);
|
|
304
|
-
if (this.xrViewerPose) {
|
|
305
|
-
this.updateViews();
|
|
306
|
-
if (this.leftView == null || this.rightView == null) {
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
this.sendXRDataToUE();
|
|
310
|
-
this.updateVideoTexture();
|
|
311
|
-
this.render();
|
|
312
|
-
}
|
|
313
|
-
if (this.webRtcController.config.isFlagEnabled(pixelstreamingfrontend_1.Flags.XRControllerInput)) {
|
|
314
|
-
this.xrSession.inputSources.forEach((source, _index, _array) => {
|
|
315
|
-
this.xrGamepadController.updateStatus(source, frame, this.xrRefSpace);
|
|
316
|
-
}, this);
|
|
317
|
-
}
|
|
318
|
-
this.xrSession.requestAnimationFrame((time, frame) => this.onXrFrame(time, frame));
|
|
319
|
-
this.onFrame.dispatchEvent(new EventEmitter_1.XrFrameEvent({ time, frame }));
|
|
320
|
-
}
|
|
321
|
-
updateViews() {
|
|
322
|
-
if (!this.xrViewerPose) {
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
for (const view of this.xrViewerPose.views) {
|
|
326
|
-
if (view.eye === 'left') {
|
|
327
|
-
this.leftView = view;
|
|
328
|
-
}
|
|
329
|
-
else if (view.eye === 'right') {
|
|
330
|
-
this.rightView = view;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
render() {
|
|
335
|
-
if (!this.gl) {
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
// Bind the framebuffer to the base layer's framebuffer
|
|
339
|
-
const glLayer = this.xrSession.renderState.baseLayer;
|
|
340
|
-
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, glLayer.framebuffer);
|
|
341
|
-
// Set the relevant portion of clip space
|
|
342
|
-
this.gl.viewport(0, 0, glLayer.framebufferWidth, glLayer.framebufferHeight);
|
|
343
|
-
// Draw the rectangle we will show the video stream texture on
|
|
344
|
-
this.gl.drawArrays(this.gl.TRIANGLES /*primitiveType*/, 0 /*offset*/, 6 /*count*/);
|
|
345
|
-
}
|
|
346
|
-
static isSessionSupported(mode) {
|
|
347
|
-
if (location.protocol !== 'https:') {
|
|
348
|
-
lib_pixelstreamingcommon_ue5_5_1.Logger.Info('WebXR requires https, if you want WebXR use https.');
|
|
349
|
-
}
|
|
350
|
-
if (navigator.xr) {
|
|
351
|
-
return navigator.xr.isSessionSupported(mode);
|
|
352
|
-
}
|
|
353
|
-
else {
|
|
354
|
-
return new Promise(() => {
|
|
355
|
-
return false;
|
|
356
|
-
});
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
exports.WebXRController = WebXRController;
|
|
92
|
+
`;
|
|
93
|
+
// setup vertex shader
|
|
94
|
+
const vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
|
|
95
|
+
this.gl.shaderSource(vertexShader, vertexShaderSource);
|
|
96
|
+
this.gl.compileShader(vertexShader);
|
|
97
|
+
// setup fragment shader
|
|
98
|
+
const fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
|
|
99
|
+
this.gl.shaderSource(fragmentShader, fragmentShaderSource);
|
|
100
|
+
this.gl.compileShader(fragmentShader);
|
|
101
|
+
// setup GLSL program
|
|
102
|
+
const shaderProgram = this.gl.createProgram();
|
|
103
|
+
this.gl.attachShader(shaderProgram, vertexShader);
|
|
104
|
+
this.gl.attachShader(shaderProgram, fragmentShader);
|
|
105
|
+
this.gl.linkProgram(shaderProgram);
|
|
106
|
+
this.gl.useProgram(shaderProgram);
|
|
107
|
+
// look up where vertex data needs to go
|
|
108
|
+
this.positionLocation = this.gl.getAttribLocation(shaderProgram, 'a_position');
|
|
109
|
+
this.texcoordLocation = this.gl.getAttribLocation(shaderProgram, 'a_texCoord');
|
|
110
|
+
}
|
|
111
|
+
updateVideoTexture() {
|
|
112
|
+
if (!this.videoTexture) {
|
|
113
|
+
// Create our texture that we use in our shader
|
|
114
|
+
// and bind it once because we never use any other texture.
|
|
115
|
+
this.videoTexture = this.gl.createTexture();
|
|
116
|
+
this.gl.bindTexture(this.gl.TEXTURE_2D, this.videoTexture);
|
|
117
|
+
// Set the parameters so we can render any size image.
|
|
118
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
|
|
119
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
|
|
120
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
|
|
121
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
|
|
122
|
+
}
|
|
123
|
+
const videoHeight = this.webRtcController.videoPlayer.getVideoElement().videoHeight;
|
|
124
|
+
const videoWidth = this.webRtcController.videoPlayer.getVideoElement().videoWidth;
|
|
125
|
+
if (this.prevVideoHeight != videoHeight || this.prevVideoWidth != videoWidth) {
|
|
126
|
+
// Do full update of texture if dimensions do not match
|
|
127
|
+
this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, videoWidth, videoHeight, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, this.webRtcController.videoPlayer.getVideoElement());
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// If dimensions match just update the sub region
|
|
131
|
+
this.gl.texSubImage2D(this.gl.TEXTURE_2D, 0, 0, 0, videoWidth, videoHeight, this.gl.RGBA, this.gl.UNSIGNED_BYTE, this.webRtcController.videoPlayer.getVideoElement());
|
|
132
|
+
}
|
|
133
|
+
// Update prev video width/height
|
|
134
|
+
this.prevVideoHeight = videoHeight;
|
|
135
|
+
this.prevVideoWidth = videoWidth;
|
|
136
|
+
}
|
|
137
|
+
initBuffers() {
|
|
138
|
+
// Create out position buffer and its vertex shader attribute
|
|
139
|
+
{
|
|
140
|
+
// Create a buffer to put the the vertices of the plane we will draw the video stream onto
|
|
141
|
+
this.positionBuffer = this.gl.createBuffer();
|
|
142
|
+
// Bind the position buffer
|
|
143
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
|
|
144
|
+
// Enable `positionLocation` to be used as vertex shader attribute
|
|
145
|
+
this.gl.enableVertexAttribArray(this.positionLocation);
|
|
146
|
+
// Note: positions are passed in clip-space coordinates [-1..1] so no need to convert in-shader
|
|
147
|
+
// prettier-ignore
|
|
148
|
+
this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([
|
|
149
|
+
-1.0, 1.0,
|
|
150
|
+
1.0, 1.0,
|
|
151
|
+
-1.0, -1.0,
|
|
152
|
+
-1.0, -1.0,
|
|
153
|
+
1.0, 1.0,
|
|
154
|
+
1.0, -1.0
|
|
155
|
+
]), this.gl.STATIC_DRAW);
|
|
156
|
+
// Tell position attribute of the vertex shader how to get data out of the bound buffer (the positionBuffer)
|
|
157
|
+
this.gl.vertexAttribPointer(this.positionLocation, 2 /*size*/, this.gl.FLOAT /*type*/, false /*normalize*/, 0 /*stride*/, 0 /*offset*/);
|
|
158
|
+
}
|
|
159
|
+
// Create our texture coordinate buffers for accessing our texture
|
|
160
|
+
{
|
|
161
|
+
this.texcoordBuffer = this.gl.createBuffer();
|
|
162
|
+
// Bind the texture coordinate buffer
|
|
163
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texcoordBuffer);
|
|
164
|
+
// Enable `texcoordLocation` to be used as a vertex shader attribute
|
|
165
|
+
this.gl.enableVertexAttribArray(this.texcoordLocation);
|
|
166
|
+
// The texture coordinates to apply for rectangle we are drawing
|
|
167
|
+
this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0]), this.gl.STATIC_DRAW);
|
|
168
|
+
// Tell texture coordinate attribute of the vertex shader how to get data out of the bound buffer (the texcoordBuffer)
|
|
169
|
+
this.gl.vertexAttribPointer(this.texcoordLocation, 2 /*size*/, this.gl.FLOAT /*type*/, false /*normalize*/, 0 /*stride*/, 0 /*offset*/);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
onXrSessionStarted(session) {
|
|
173
|
+
lib_pixelstreamingcommon_ue5_5_1.Logger.Info('XR Session started');
|
|
174
|
+
this.xrSession = session;
|
|
175
|
+
this.xrSession.addEventListener('end', () => {
|
|
176
|
+
this.onXrSessionEnded();
|
|
177
|
+
});
|
|
178
|
+
// Initialization
|
|
179
|
+
this.initGL();
|
|
180
|
+
this.initShaders();
|
|
181
|
+
this.initBuffers();
|
|
182
|
+
session.requestReferenceSpace('local').then((refSpace) => {
|
|
183
|
+
this.xrRefSpace = refSpace;
|
|
184
|
+
// Set up our base layer (i.e. a projection layer that fills the entire XR viewport).
|
|
185
|
+
this.xrSession.updateRenderState({
|
|
186
|
+
baseLayer: new XRWebGLLayer(this.xrSession, this.gl)
|
|
187
|
+
});
|
|
188
|
+
// Update target framerate to 90 fps if 90 fps is supported in this XR device
|
|
189
|
+
if (this.xrSession.supportedFrameRates) {
|
|
190
|
+
for (const frameRate of this.xrSession.supportedFrameRates) {
|
|
191
|
+
if (frameRate == 90) {
|
|
192
|
+
session.updateTargetFrameRate(90);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// Binding to each new frame to get latest XR updates
|
|
197
|
+
this.xrSession.requestAnimationFrame(this.onXrFrame.bind(this));
|
|
198
|
+
});
|
|
199
|
+
this.onSessionStarted.dispatchEvent(new Event('xrSessionStarted'));
|
|
200
|
+
}
|
|
201
|
+
areArraysEqual(a, b) {
|
|
202
|
+
return (a.length === b.length && a.every((element, index) => Math.abs(element - b[index]) <= this.EPSILON));
|
|
203
|
+
}
|
|
204
|
+
arePointsEqual(a, b) {
|
|
205
|
+
return (Math.abs(a.x - b.x) >= this.EPSILON &&
|
|
206
|
+
Math.abs(a.y - b.y) >= this.EPSILON &&
|
|
207
|
+
Math.abs(a.z - b.z) >= this.EPSILON);
|
|
208
|
+
}
|
|
209
|
+
sendXRDataToUE() {
|
|
210
|
+
if (this.leftView == null || this.rightView == null) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
// We selectively send either the `XREyeViews` or `XRHMDTransform`
|
|
214
|
+
// messages over the datachannel. The reason for this selective sending is that
|
|
215
|
+
// the `XREyeViews` is a much larger message and changes infrequently (e.g. only when user changes headset IPD).
|
|
216
|
+
// Therefore, we only need to send it once on startup and then any time it changes.
|
|
217
|
+
// The rest of the time we can send the `XRHMDTransform` message.
|
|
218
|
+
let shouldSendEyeViews = this.lastSentLeftEyeProj == null ||
|
|
219
|
+
this.lastSentRightEyeProj == null ||
|
|
220
|
+
this.lastSentRelativeLeftEyePos == null ||
|
|
221
|
+
this.lastSentRelativeRightEyePos == null;
|
|
222
|
+
const leftEyeTrans = this.leftView.transform.matrix;
|
|
223
|
+
const leftEyeProj = this.leftView.projectionMatrix;
|
|
224
|
+
const rightEyeTrans = this.rightView.transform.matrix;
|
|
225
|
+
const rightEyeProj = this.rightView.projectionMatrix;
|
|
226
|
+
const hmdTrans = this.xrViewerPose.transform.matrix;
|
|
227
|
+
// Check if projection matrices have changed
|
|
228
|
+
if (!shouldSendEyeViews && this.lastSentLeftEyeProj != null && this.lastSentRightEyeProj != null) {
|
|
229
|
+
const leftEyeProjUnchanged = this.areArraysEqual(leftEyeProj, this.lastSentLeftEyeProj);
|
|
230
|
+
const rightEyeProjUnchanged = this.areArraysEqual(rightEyeProj, this.lastSentRightEyeProj);
|
|
231
|
+
shouldSendEyeViews = leftEyeProjUnchanged == false || rightEyeProjUnchanged == false;
|
|
232
|
+
}
|
|
233
|
+
const leftEyeRelativePos = new DOMPointReadOnly(this.leftView.transform.position.x - this.xrViewerPose.transform.position.x, this.leftView.transform.position.y - this.xrViewerPose.transform.position.y, this.leftView.transform.position.z - this.xrViewerPose.transform.position.z, 1.0);
|
|
234
|
+
const rightEyeRelativePos = new DOMPointReadOnly(this.leftView.transform.position.x - this.xrViewerPose.transform.position.x, this.leftView.transform.position.y - this.xrViewerPose.transform.position.y, this.leftView.transform.position.z - this.xrViewerPose.transform.position.z, 1.0);
|
|
235
|
+
// Check if relative eye pos has changed (e.g IPD changed)
|
|
236
|
+
if (!shouldSendEyeViews &&
|
|
237
|
+
this.lastSentRelativeLeftEyePos != null &&
|
|
238
|
+
this.lastSentRelativeRightEyePos != null) {
|
|
239
|
+
const leftEyePosUnchanged = this.arePointsEqual(leftEyeRelativePos, this.lastSentRelativeLeftEyePos);
|
|
240
|
+
const rightEyePosUnchanged = this.arePointsEqual(rightEyeRelativePos, this.lastSentRelativeRightEyePos);
|
|
241
|
+
shouldSendEyeViews = leftEyePosUnchanged == false || rightEyePosUnchanged == false;
|
|
242
|
+
// Note: We are not checking if EyeView rotation changes (as far as I know no HMD supports changing this value at runtime).
|
|
243
|
+
}
|
|
244
|
+
if (shouldSendEyeViews) {
|
|
245
|
+
// send transform (4x4) and projection matrix (4x4) data for each eye (left first, then right)
|
|
246
|
+
// prettier-ignore
|
|
247
|
+
this.webRtcController.streamMessageController.toStreamerHandlers.get('XREyeViews')([
|
|
248
|
+
// Left eye 4x4 transform matrix
|
|
249
|
+
leftEyeTrans[0], leftEyeTrans[4], leftEyeTrans[8], leftEyeTrans[12],
|
|
250
|
+
leftEyeTrans[1], leftEyeTrans[5], leftEyeTrans[9], leftEyeTrans[13],
|
|
251
|
+
leftEyeTrans[2], leftEyeTrans[6], leftEyeTrans[10], leftEyeTrans[14],
|
|
252
|
+
leftEyeTrans[3], leftEyeTrans[7], leftEyeTrans[11], leftEyeTrans[15],
|
|
253
|
+
// Left eye 4x4 projection matrix
|
|
254
|
+
leftEyeProj[0], leftEyeProj[4], leftEyeProj[8], leftEyeProj[12],
|
|
255
|
+
leftEyeProj[1], leftEyeProj[5], leftEyeProj[9], leftEyeProj[13],
|
|
256
|
+
leftEyeProj[2], leftEyeProj[6], leftEyeProj[10], leftEyeProj[14],
|
|
257
|
+
leftEyeProj[3], leftEyeProj[7], leftEyeProj[11], leftEyeProj[15],
|
|
258
|
+
// Right eye 4x4 transform matrix
|
|
259
|
+
rightEyeTrans[0], rightEyeTrans[4], rightEyeTrans[8], rightEyeTrans[12],
|
|
260
|
+
rightEyeTrans[1], rightEyeTrans[5], rightEyeTrans[9], rightEyeTrans[13],
|
|
261
|
+
rightEyeTrans[2], rightEyeTrans[6], rightEyeTrans[10], rightEyeTrans[14],
|
|
262
|
+
rightEyeTrans[3], rightEyeTrans[7], rightEyeTrans[11], rightEyeTrans[15],
|
|
263
|
+
// right eye 4x4 projection matrix
|
|
264
|
+
rightEyeProj[0], rightEyeProj[4], rightEyeProj[8], rightEyeProj[12],
|
|
265
|
+
rightEyeProj[1], rightEyeProj[5], rightEyeProj[9], rightEyeProj[13],
|
|
266
|
+
rightEyeProj[2], rightEyeProj[6], rightEyeProj[10], rightEyeProj[14],
|
|
267
|
+
rightEyeProj[3], rightEyeProj[7], rightEyeProj[11], rightEyeProj[15],
|
|
268
|
+
// HMD 4x4 transform
|
|
269
|
+
hmdTrans[0], hmdTrans[4], hmdTrans[8], hmdTrans[12],
|
|
270
|
+
hmdTrans[1], hmdTrans[5], hmdTrans[9], hmdTrans[13],
|
|
271
|
+
hmdTrans[2], hmdTrans[6], hmdTrans[10], hmdTrans[14],
|
|
272
|
+
hmdTrans[3], hmdTrans[7], hmdTrans[11], hmdTrans[15],
|
|
273
|
+
]);
|
|
274
|
+
this.lastSentLeftEyeProj = leftEyeProj;
|
|
275
|
+
this.lastSentRightEyeProj = rightEyeProj;
|
|
276
|
+
this.lastSentRelativeLeftEyePos = leftEyeRelativePos;
|
|
277
|
+
this.lastSentRelativeRightEyePos = rightEyeRelativePos;
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
// If we don't need to the entire eye views being sent just send the HMD transform
|
|
281
|
+
this.webRtcController.streamMessageController.toStreamerHandlers.get('XRHMDTransform')([
|
|
282
|
+
// HMD 4x4 transform
|
|
283
|
+
hmdTrans[0],
|
|
284
|
+
hmdTrans[4],
|
|
285
|
+
hmdTrans[8],
|
|
286
|
+
hmdTrans[12],
|
|
287
|
+
hmdTrans[1],
|
|
288
|
+
hmdTrans[5],
|
|
289
|
+
hmdTrans[9],
|
|
290
|
+
hmdTrans[13],
|
|
291
|
+
hmdTrans[2],
|
|
292
|
+
hmdTrans[6],
|
|
293
|
+
hmdTrans[10],
|
|
294
|
+
hmdTrans[14],
|
|
295
|
+
hmdTrans[3],
|
|
296
|
+
hmdTrans[7],
|
|
297
|
+
hmdTrans[11],
|
|
298
|
+
hmdTrans[15]
|
|
299
|
+
]);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
onXrFrame(time, frame) {
|
|
303
|
+
this.xrViewerPose = frame.getViewerPose(this.xrRefSpace);
|
|
304
|
+
if (this.xrViewerPose) {
|
|
305
|
+
this.updateViews();
|
|
306
|
+
if (this.leftView == null || this.rightView == null) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
this.sendXRDataToUE();
|
|
310
|
+
this.updateVideoTexture();
|
|
311
|
+
this.render();
|
|
312
|
+
}
|
|
313
|
+
if (this.webRtcController.config.isFlagEnabled(pixelstreamingfrontend_1.Flags.XRControllerInput)) {
|
|
314
|
+
this.xrSession.inputSources.forEach((source, _index, _array) => {
|
|
315
|
+
this.xrGamepadController.updateStatus(source, frame, this.xrRefSpace);
|
|
316
|
+
}, this);
|
|
317
|
+
}
|
|
318
|
+
this.xrSession.requestAnimationFrame((time, frame) => this.onXrFrame(time, frame));
|
|
319
|
+
this.onFrame.dispatchEvent(new EventEmitter_1.XrFrameEvent({ time, frame }));
|
|
320
|
+
}
|
|
321
|
+
updateViews() {
|
|
322
|
+
if (!this.xrViewerPose) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
for (const view of this.xrViewerPose.views) {
|
|
326
|
+
if (view.eye === 'left') {
|
|
327
|
+
this.leftView = view;
|
|
328
|
+
}
|
|
329
|
+
else if (view.eye === 'right') {
|
|
330
|
+
this.rightView = view;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
render() {
|
|
335
|
+
if (!this.gl) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
// Bind the framebuffer to the base layer's framebuffer
|
|
339
|
+
const glLayer = this.xrSession.renderState.baseLayer;
|
|
340
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, glLayer.framebuffer);
|
|
341
|
+
// Set the relevant portion of clip space
|
|
342
|
+
this.gl.viewport(0, 0, glLayer.framebufferWidth, glLayer.framebufferHeight);
|
|
343
|
+
// Draw the rectangle we will show the video stream texture on
|
|
344
|
+
this.gl.drawArrays(this.gl.TRIANGLES /*primitiveType*/, 0 /*offset*/, 6 /*count*/);
|
|
345
|
+
}
|
|
346
|
+
static isSessionSupported(mode) {
|
|
347
|
+
if (location.protocol !== 'https:') {
|
|
348
|
+
lib_pixelstreamingcommon_ue5_5_1.Logger.Info('WebXR requires https, if you want WebXR use https.');
|
|
349
|
+
}
|
|
350
|
+
if (navigator.xr) {
|
|
351
|
+
return navigator.xr.isSessionSupported(mode);
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
return new Promise(() => {
|
|
355
|
+
return false;
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
exports.WebXRController = WebXRController;
|
|
361
361
|
//# sourceMappingURL=WebXRController.js.map
|