-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathThreejsInterface.js
More file actions
158 lines (142 loc) · 5.89 KB
/
ThreejsInterface.js
File metadata and controls
158 lines (142 loc) · 5.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import {WebGLStrategy} from "./glCommandBuffer.js";
/**
* @typedef {import("./object.js").SpatialInterface} SpatialInterface
* @typedef {import("./WorkerFactory.js").MessageInterface} MessageInterface
*/
/**
* this interface mediates between the server, spatialinterface and the webworker
*/
class ThreejsInterface {
/**
* @param {SpatialInterface} spatialInterface
* @param {string} workerScript
*/
constructor(spatialInterface, workerScript) {
// some information will become available after the bootstrap message has been received
/**
* @type {SpatialInterface}
*/
this.spatialInterface = spatialInterface;
/**
* @type {MessageInterface}
*/
this.workerMessageInterface = WebGLStrategy.getInstance().workerFactory.createWorker(workerScript, true);
this.workerId = -1;
this.prefersAttachingToWorld = true;
this.spatialInterface.useWebGlWorker();
this.spatialInterface.onSpatialInterfaceLoaded(this.onSpatialInterfaceLoaded.bind(this));
this.spatialInterface.onWindowResized(({width, height}) => {
this.workerMessageInterface.postMessage({name: "onWindowResized", width: width, height: height});
// trigger resending of projection matrix
this.spatialInterface.subscribeToMatrix();
});
/**
* @type {Int32Array|null}
*/
this.synclock = null;
this.touchAnswerListener = null;
this.mouse = {x: 0, y: 0};
this.lastTouchResult = false;
}
getWorkerMessageInterface() {
return this.workerMessageInterface;
}
/**
* this message is used to setup the projection matrix in the webworker
* @param {Float32Array} modelViewMatrix
* @param {Float32Array} projectionMatrix
*/
anchoredModelViewCallback(modelViewMatrix, projectionMatrix) {
this.workerMessageInterface.postMessage({name: "anchoredModelViewCallback", projectionMatrix: projectionMatrix});
}
/**
* after loading the spatial interface the worker can finish it's configuration
*/
onSpatialInterfaceLoaded() {
this.spatialInterface.subscribeToMatrix();
this.spatialInterface.setFullScreenOn();
if (this.prefersAttachingToWorld) {
this.spatialInterface.prefersAttachingToWorld();
}
this.spatialInterface.addAnchoredModelViewListener(this.anchoredModelViewCallback.bind(this));
this.spatialInterface.setMoveDelay(300);
this.spatialInterface.registerTouchDecider(this.touchDecider.bind(this));
}
/**
* receives messages from the webworker to send to the server
* @param {MessageEvent<any>} event
*/
onMessageFromWorker(event) {
const message = event.data;
if (message) {
if ((this.touchAnswerListener !== null) && (typeof message === 'object') && (message.name === "touchDeciderAnswer")) {
this.lastTouchResult = message.result;
this.touchAnswerListener(true);
} else {
self.parent.postMessage(message, "*");
}
}
}
/**
* receives messages from the server to send through to the webworker
* some messages influence the spatial interface and are intercepted before passing them on
* @param {MessageEvent<any>} event
*/
onMessageFromServer(event) {
const message = event.data;
if (message && (typeof message === 'object')) {
if (message.hasOwnProperty("name")) {
if (message.name === "bootstrap") {
// finish initalisation of the client
const {workerId, width, height} = message;
this.workerId = workerId;
this.synclock = message.synclock;
this.spatialInterface.changeFrameSize(width, height);
}
}
if ((this.synclock !== null) && Atomics.load(this.synclock, 0) === 0) {
// the webworker is in a locked state and unable to process incomming messages, drop the message and send an end frame message to make the server continue with the next proxy
console.log("worker " + this.workerId + " is waiting, dropping message");
if (message.hasOwnProperty("name") && (message.name === "frame")) {
self.parent.postMessage({
workerId: this.workerId,
isFrameEnd: true,
});
}
} else {
this.workerMessageInterface.postMessage(message);
}
}
}
/**
* @returns {Promise<boolean>}
*/
makeWatchdog() {
return new Promise((res) => {
setTimeout(res, 3000, false);
});
}
async touchDecider(eventData) {
//1. sets the mouse position with a coordinate system where the center
// of the screen is the origin
this.mouse.x = (eventData.x / window.innerWidth) * 2 - 1;
this.mouse.y = -(eventData.y / window.innerHeight) * 2 + 1;
// if the webworker (containing the renderer) isn't sleeping, post touch message to analyse
if ((this.synclock !== null) && Atomics.load(this.synclock, 0) === 0) {
console.warn("tocuh decider locked worker, returning no touch");
return false;
}
this.workerMessageInterface.postMessage({name: "touchDecider", mouse: this.mouse, workerId: this.workerId});
let res = await Promise.race([this.makeWatchdog(), new Promise((result) => {
this.touchAnswerListener = result;
})]);
if (!res) {
console.warn("touch decider timeout, returning no touch");
this.touchAnswerListener = null;
return false;
}
this.touchAnswerListener = null;
return this.lastTouchResult;
}
}
export {ThreejsInterface};