teleportxr 1.0.21 → 1.0.22
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/.clang-format +21 -21
- package/.vscode/launch.json +28 -18
- package/.vscode/settings.json +12 -12
- package/LICENSE +21 -21
- package/LICENSES/Apache-2.0.txt +73 -73
- package/LICENSES/BSD-3-Clause.txt +11 -11
- package/LICENSES/BSL-1.0.txt +7 -7
- package/LICENSES/CC-BY-4.0.txt +156 -156
- package/LICENSES/CC0-1.0.txt +121 -121
- package/LICENSES/MIT.txt +9 -9
- package/README.md +6 -6
- package/assets/scene.json +8 -8
- package/client/client.js +441 -437
- package/client/client_manager.js +124 -124
- package/client/geometry_service.js +447 -447
- package/connections/webrtcconnection.js +437 -437
- package/connections/webrtcconnectionmanager.js +95 -95
- package/core/core.js +447 -447
- package/index.js +31 -13
- package/package.json +40 -40
- package/protocol/command.js +146 -146
- package/protocol/encoders/node_encoder.js +27 -27
- package/protocol/encoders/resource_encoder.js +29 -29
- package/protocol/message.js +171 -171
- package/scene/material.js +104 -104
- package/scene/node.js +355 -355
- package/scene/resources.js +241 -241
- package/scene/scene.js +269 -262
- package/signaling.js +275 -273
- package/test.json +1 -1
- package/test.ps1 +2 -0
- package/utils/convert_fbx_anims.js +72 -0
- package/utils/fbx-to-vrma.js +1532 -0
- package/utils/upgrade-vrms.js +274 -0
- package/utils/vrm-upgrader.js +654 -0
- package/temp.js +0 -1
package/client/client.js
CHANGED
|
@@ -1,437 +1,441 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const core= require("../core/core.js");
|
|
4
|
-
const command= require("../protocol/command.js");
|
|
5
|
-
const message= require("../protocol/message.js");
|
|
6
|
-
const gs= require("./geometry_service.js");
|
|
7
|
-
const node_encoder= require("../protocol/encoders/node_encoder.js");
|
|
8
|
-
const resource_encoder= require("../protocol/encoders/resource_encoder.js");
|
|
9
|
-
const WebRtcConnectionManager = require('../connections/webrtcconnectionmanager');
|
|
10
|
-
const resources= require("../scene/resources.js");
|
|
11
|
-
const { BackgroundMode } = require("../core/core.js");
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class OriginState
|
|
15
|
-
{
|
|
16
|
-
constructor() {
|
|
17
|
-
this.sent=false;
|
|
18
|
-
this.originClientHas=BigInt(0);
|
|
19
|
-
this.ackId=0;
|
|
20
|
-
this.acknowledged=false;
|
|
21
|
-
this.serverTimeSentUs=BigInt(0);
|
|
22
|
-
this.valid_counter=BigInt(0);
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
class LightingState
|
|
27
|
-
{
|
|
28
|
-
constructor() {
|
|
29
|
-
this.ackId=0;
|
|
30
|
-
this.acknowledged=false;
|
|
31
|
-
this.serverTimeSentUs=BigInt(0);
|
|
32
|
-
this.clientDynamicLighting=new core.ClientDynamicLighting();
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
class Client {
|
|
37
|
-
constructor(cid,sigSend) {
|
|
38
|
-
this.signalingSend=sigSend;
|
|
39
|
-
this.clientID=cid;
|
|
40
|
-
this.origin_uid=0;
|
|
41
|
-
this.handshakeMessage=new message.HandshakeMessage();
|
|
42
|
-
this.geometryService=new gs.GeometryService(cid);
|
|
43
|
-
this.webRtcConnected=false;
|
|
44
|
-
this.webRtcConnection=null;
|
|
45
|
-
this.currentOriginState=new OriginState();
|
|
46
|
-
this.currentLightingState=new LightingState();
|
|
47
|
-
this.next_ack_id=BigInt(1);
|
|
48
|
-
}
|
|
49
|
-
tick(timestamp){
|
|
50
|
-
this.geometryService.GetNodesToSend();
|
|
51
|
-
}
|
|
52
|
-
streamingConnectionStateChanged(newState)
|
|
53
|
-
{
|
|
54
|
-
//this.webRtcConnection=wrtcConn;
|
|
55
|
-
// This should have come from our own existing webRtcConnection and nowhere else.
|
|
56
|
-
console.log("Connection state is "+newState.toString());
|
|
57
|
-
if(newState=="connected")
|
|
58
|
-
{
|
|
59
|
-
//this.webRtcConnection.sendGeometry("test");
|
|
60
|
-
this.webRtcConnected=true;
|
|
61
|
-
}
|
|
62
|
-
else
|
|
63
|
-
{
|
|
64
|
-
this.webRtcConnected=false;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
receivedMessageReliable(id,data)
|
|
68
|
-
{
|
|
69
|
-
console.log('Client receivedMessage reliable ch.'+id+' received: '+data+'.');
|
|
70
|
-
}
|
|
71
|
-
// Is the Buffer bf too small to contain a type tp?
|
|
72
|
-
checkTooSmall(tp,bf) {
|
|
73
|
-
if(bf.byteLength<tp.sizeof()) {
|
|
74
|
-
// NOTE: we use log() here rather than warn() or error() because this is a problem with
|
|
75
|
-
// the message we were SENT, not with the local server code.
|
|
76
|
-
console.log("Binary message from "+this.clientID+" is too small to be a "+tp.toString()+": "+bf.byteLength+"<"+tp.sizeof());
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
return true;
|
|
80
|
-
}
|
|
81
|
-
receiveReceivedResourcesMessage(bf)
|
|
82
|
-
{
|
|
83
|
-
if(!this.checkTooSmall(message.ReceivedResourcesMessage,bf)) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
var msg =new message.ReceivedResourcesMessage();
|
|
87
|
-
var uia =new Uint8Array(bf);
|
|
88
|
-
var dataView =new DataView(bf,0,bf.length);
|
|
89
|
-
core.decodeFromDataView(msg,dataView,0);
|
|
90
|
-
const excess =uia.length-message.ReceivedResourcesMessage.sizeof();
|
|
91
|
-
const numReceived =excess/core.UID_SIZE;
|
|
92
|
-
if(numReceived!=msg.uint64_receivedResourcesCount) {
|
|
93
|
-
console.log("ReceivedResourcesMessage claims to have "<<msg.resourceCount<<" resources but has only enough data for "+numReceived);
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
var offset =message.ReceivedResourcesMessage.sizeof();
|
|
97
|
-
for(let i=0;i<msg.uint64_receivedResourcesCount;i++ ) {
|
|
98
|
-
var uid=dataView.getBigUint64(offset,core.endian);
|
|
99
|
-
this.geometryService.ConfirmResource(uid);
|
|
100
|
-
offset+=core.UID_SIZE;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
receivedMessageUnreliable(id,pkt)
|
|
104
|
-
{
|
|
105
|
-
var dataView=new DataView(pkt.data,0,1);
|
|
106
|
-
const messageType=dataView.getUint8(0);
|
|
107
|
-
//console.log('Client receivedMessage reliable ch.'+id+' received: '+messageType+'.');
|
|
108
|
-
switch(messageType){
|
|
109
|
-
case message.MessagePayloadType.ReceivedResources:
|
|
110
|
-
this.receiveReceivedResourcesMessage(pkt.data);
|
|
111
|
-
return;
|
|
112
|
-
case message.MessagePayloadType.Acknowledgement:
|
|
113
|
-
this.ReceiveAcknowledgement(pkt.data);
|
|
114
|
-
return;
|
|
115
|
-
case message.MessagePayloadType.ControllerPoses:
|
|
116
|
-
this.ReceiveNodePoses(pkt.data);
|
|
117
|
-
return;
|
|
118
|
-
default:
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
// We call Start() when the signaling server accepts the client.
|
|
123
|
-
// In Start() we send the SetupCommand.
|
|
124
|
-
Start()
|
|
125
|
-
{
|
|
126
|
-
this.setupCommand=new command.SetupCommand();
|
|
127
|
-
this.clientDynamicLighting=new core.ClientDynamicLighting();
|
|
128
|
-
this.setupCommand.float32_draw_distance=10.0;
|
|
129
|
-
if(this.scene)
|
|
130
|
-
{
|
|
131
|
-
if(this.scene.backgroundTexturePath&&this.scene.backgroundTexturePath!="")
|
|
132
|
-
{
|
|
133
|
-
this.setupCommand.BackgroundMode_backgroundMode=BackgroundMode.TEXTURE;
|
|
134
|
-
this.setupCommand.uid_backgroundTexture=resources.AddTexture(this.scene.backgroundTexturePath);
|
|
135
|
-
}
|
|
136
|
-
if(this.scene.diffuseCubemapPath&&this.scene.diffuseCubemapPath!="")
|
|
137
|
-
{
|
|
138
|
-
this.clientDynamicLighting.uid_diffuse_cubemap_texture_uid=resources.AddTexture(this.scene.diffuseCubemapPath);
|
|
139
|
-
}
|
|
140
|
-
if(this.scene.specularCubemapPath&&this.scene.specularCubemapPath!="")
|
|
141
|
-
{
|
|
142
|
-
this.clientDynamicLighting.uid_specular_cubemap_texture_uid=resources.AddTexture(this.scene.specularCubemapPath);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
this.SendCommand(this.setupCommand);
|
|
146
|
-
}
|
|
147
|
-
SendCommand(command){
|
|
148
|
-
let array=core.encodeToUint8Array(command);
|
|
149
|
-
this.signalingSend(array);
|
|
150
|
-
}
|
|
151
|
-
// We call StartStreaming once the SetupCommand has been acknowledged.
|
|
152
|
-
StartStreaming()
|
|
153
|
-
{
|
|
154
|
-
this.webRtcConnectionManager=WebRtcConnectionManager.getInstance();
|
|
155
|
-
// We make sure WebRTC has a connection for this client.
|
|
156
|
-
this.webRtcConnection = this.webRtcConnectionManager.createConnection(
|
|
157
|
-
this.clientID
|
|
158
|
-
,this.streamingConnectionStateChanged.bind(this)
|
|
159
|
-
,this.receivedMessageReliable.bind(this)
|
|
160
|
-
,this.receivedMessageUnreliable.bind(this));
|
|
161
|
-
//.then(
|
|
162
|
-
// function(value) {myDisplayer(value);},
|
|
163
|
-
// function(error) {myDisplayer(error);}
|
|
164
|
-
}
|
|
165
|
-
//! Cleanly shut down the WebRTC connection. This may be called when the client has signalled that it is
|
|
166
|
-
//! disconnecting, or when the server determines that the client is lost or needs to be booted.
|
|
167
|
-
StopStreaming()
|
|
168
|
-
{
|
|
169
|
-
this.webRtcConnectionManager.destroyConnection(this.clientID);
|
|
170
|
-
this.webRtcConnection=null;
|
|
171
|
-
}
|
|
172
|
-
// Generic message acknowledgement. Certain kinds of message are expected to be ack'ed.
|
|
173
|
-
ReceiveAcknowledgement(data)
|
|
174
|
-
{
|
|
175
|
-
if (data.byteLength!=message.AcknowledgementMessage.sizeof())
|
|
176
|
-
{
|
|
177
|
-
console.log("Client: Received malformed AcknowledgementMessage packet of length: ",data.length);
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
var msg =new message.AcknowledgementMessage();
|
|
181
|
-
var bf =data;
|
|
182
|
-
var dataView =new DataView(data,0,data.length);
|
|
183
|
-
core.decodeFromDataView(msg,dataView,0);
|
|
184
|
-
if(msg.uint64_ackId==this.currentOriginState.ackId)
|
|
185
|
-
{
|
|
186
|
-
this.currentOriginState.acknowledged=true;
|
|
187
|
-
}
|
|
188
|
-
if(msg.uint64_ackId==this.currentLightingState.ackId)
|
|
189
|
-
{
|
|
190
|
-
this.currentLightingState.acknowledged=true;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
ReceiveNodePoses(data)
|
|
194
|
-
{
|
|
195
|
-
if (data.byteLength<message.NodePosesMessage.sizeof())
|
|
196
|
-
{
|
|
197
|
-
console.log("Client: Received malformed NodePosesMessage packet of length: ",data.length);
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
var msg =new message.NodePosesMessage();
|
|
201
|
-
var dataView =new DataView(data,0,data.byteLength);
|
|
202
|
-
var byteOffset =0;
|
|
203
|
-
msg.messageType = dataView.getUint8(byteOffset, core.endian);
|
|
204
|
-
msg.timestamp = dataView.getBigUint64(byteOffset+1, core.endian);
|
|
205
|
-
byteOffset =msg.Pose_headPose.decodeOrientationPositionFromDataView(
|
|
206
|
-
msg.uint16_numPoses = dataView.getUint16(byteOffset, core.endian);
|
|
207
|
-
byteOffset+=2;
|
|
208
|
-
if (data.byteLength!=message.NodePosesMessage.sizeof()+msg.uint16_numPoses*28)
|
|
209
|
-
{
|
|
210
|
-
console.log("Client: Received malformed NodePosesMessage packet of length: ",data.length);
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
msg.nodePoses = new Array(msg.uint16_numPoses);
|
|
214
|
-
for(let i=0; i <msg.uint16_numPoses; i++)
|
|
215
|
-
{
|
|
216
|
-
msg.nodePoses[i] = new NodePoseDynamic();
|
|
217
|
-
byteOffset = msg.nodePoses[i].decodeOrientationPositionFromDataView(dataView,byteOffset);
|
|
218
|
-
}
|
|
219
|
-
console.log("Client: Received ", msg.uint16_numPoses, " node poses.");
|
|
220
|
-
this.ProcessNodePoses(msg.Pose_headPose,msg.uint16_numPoses, msg.nodePoses)
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
{
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
{
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
if(this.
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
this.currentOriginState.
|
|
293
|
-
this.currentOriginState.
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
this.
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
{
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
this.
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
this.
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
this.
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
{
|
|
369
|
-
this.SendGenericResource(uid);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
{
|
|
373
|
-
this.SendGenericResource(uid);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
{
|
|
377
|
-
this.SendGenericResource(uid);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
this.currentOriginState.originClientHas
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const core= require("../core/core.js");
|
|
4
|
+
const command= require("../protocol/command.js");
|
|
5
|
+
const message= require("../protocol/message.js");
|
|
6
|
+
const gs= require("./geometry_service.js");
|
|
7
|
+
const node_encoder= require("../protocol/encoders/node_encoder.js");
|
|
8
|
+
const resource_encoder= require("../protocol/encoders/resource_encoder.js");
|
|
9
|
+
const WebRtcConnectionManager = require('../connections/webrtcconnectionmanager');
|
|
10
|
+
const resources= require("../scene/resources.js");
|
|
11
|
+
const { BackgroundMode } = require("../core/core.js");
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class OriginState
|
|
15
|
+
{
|
|
16
|
+
constructor() {
|
|
17
|
+
this.sent=false;
|
|
18
|
+
this.originClientHas=BigInt(0);
|
|
19
|
+
this.ackId=0;
|
|
20
|
+
this.acknowledged=false;
|
|
21
|
+
this.serverTimeSentUs=BigInt(0);
|
|
22
|
+
this.valid_counter=BigInt(0);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
class LightingState
|
|
27
|
+
{
|
|
28
|
+
constructor() {
|
|
29
|
+
this.ackId=0;
|
|
30
|
+
this.acknowledged=false;
|
|
31
|
+
this.serverTimeSentUs=BigInt(0);
|
|
32
|
+
this.clientDynamicLighting=new core.ClientDynamicLighting();
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
class Client {
|
|
37
|
+
constructor(cid,sigSend) {
|
|
38
|
+
this.signalingSend=sigSend;
|
|
39
|
+
this.clientID=cid;
|
|
40
|
+
this.origin_uid=0;
|
|
41
|
+
this.handshakeMessage=new message.HandshakeMessage();
|
|
42
|
+
this.geometryService=new gs.GeometryService(cid);
|
|
43
|
+
this.webRtcConnected=false;
|
|
44
|
+
this.webRtcConnection=null;
|
|
45
|
+
this.currentOriginState=new OriginState();
|
|
46
|
+
this.currentLightingState=new LightingState();
|
|
47
|
+
this.next_ack_id=BigInt(1);
|
|
48
|
+
}
|
|
49
|
+
tick(timestamp){
|
|
50
|
+
this.geometryService.GetNodesToSend();
|
|
51
|
+
}
|
|
52
|
+
streamingConnectionStateChanged(newState)
|
|
53
|
+
{
|
|
54
|
+
//this.webRtcConnection=wrtcConn;
|
|
55
|
+
// This should have come from our own existing webRtcConnection and nowhere else.
|
|
56
|
+
console.log("Connection state is "+newState.toString());
|
|
57
|
+
if(newState=="connected")
|
|
58
|
+
{
|
|
59
|
+
//this.webRtcConnection.sendGeometry("test");
|
|
60
|
+
this.webRtcConnected=true;
|
|
61
|
+
}
|
|
62
|
+
else
|
|
63
|
+
{
|
|
64
|
+
this.webRtcConnected=false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
receivedMessageReliable(id,data)
|
|
68
|
+
{
|
|
69
|
+
console.log('Client receivedMessage reliable ch.'+id+' received: '+data+'.');
|
|
70
|
+
}
|
|
71
|
+
// Is the Buffer bf too small to contain a type tp?
|
|
72
|
+
checkTooSmall(tp,bf) {
|
|
73
|
+
if(bf.byteLength<tp.sizeof()) {
|
|
74
|
+
// NOTE: we use log() here rather than warn() or error() because this is a problem with
|
|
75
|
+
// the message we were SENT, not with the local server code.
|
|
76
|
+
console.log("Binary message from "+this.clientID+" is too small to be a "+tp.toString()+": "+bf.byteLength+"<"+tp.sizeof());
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
receiveReceivedResourcesMessage(bf)
|
|
82
|
+
{
|
|
83
|
+
if(!this.checkTooSmall(message.ReceivedResourcesMessage,bf)) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
var msg =new message.ReceivedResourcesMessage();
|
|
87
|
+
var uia =new Uint8Array(bf);
|
|
88
|
+
var dataView =new DataView(bf,0,bf.length);
|
|
89
|
+
core.decodeFromDataView(msg,dataView,0);
|
|
90
|
+
const excess =uia.length-message.ReceivedResourcesMessage.sizeof();
|
|
91
|
+
const numReceived =excess/core.UID_SIZE;
|
|
92
|
+
if(numReceived!=msg.uint64_receivedResourcesCount) {
|
|
93
|
+
console.log("ReceivedResourcesMessage claims to have "<<msg.resourceCount<<" resources but has only enough data for "+numReceived);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
var offset =message.ReceivedResourcesMessage.sizeof();
|
|
97
|
+
for(let i=0;i<msg.uint64_receivedResourcesCount;i++ ) {
|
|
98
|
+
var uid=dataView.getBigUint64(offset,core.endian);
|
|
99
|
+
this.geometryService.ConfirmResource(uid);
|
|
100
|
+
offset+=core.UID_SIZE;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
receivedMessageUnreliable(id,pkt)
|
|
104
|
+
{
|
|
105
|
+
var dataView=new DataView(pkt.data,0,1);
|
|
106
|
+
const messageType=dataView.getUint8(0);
|
|
107
|
+
//console.log('Client receivedMessage reliable ch.'+id+' received: '+messageType+'.');
|
|
108
|
+
switch(messageType){
|
|
109
|
+
case message.MessagePayloadType.ReceivedResources:
|
|
110
|
+
this.receiveReceivedResourcesMessage(pkt.data);
|
|
111
|
+
return;
|
|
112
|
+
case message.MessagePayloadType.Acknowledgement:
|
|
113
|
+
this.ReceiveAcknowledgement(pkt.data);
|
|
114
|
+
return;
|
|
115
|
+
case message.MessagePayloadType.ControllerPoses:
|
|
116
|
+
this.ReceiveNodePoses(pkt.data);
|
|
117
|
+
return;
|
|
118
|
+
default:
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// We call Start() when the signaling server accepts the client.
|
|
123
|
+
// In Start() we send the SetupCommand.
|
|
124
|
+
Start()
|
|
125
|
+
{
|
|
126
|
+
this.setupCommand=new command.SetupCommand();
|
|
127
|
+
this.clientDynamicLighting=new core.ClientDynamicLighting();
|
|
128
|
+
this.setupCommand.float32_draw_distance=10.0;
|
|
129
|
+
if(this.scene)
|
|
130
|
+
{
|
|
131
|
+
if(this.scene.backgroundTexturePath&&this.scene.backgroundTexturePath!="")
|
|
132
|
+
{
|
|
133
|
+
this.setupCommand.BackgroundMode_backgroundMode=BackgroundMode.TEXTURE;
|
|
134
|
+
this.setupCommand.uid_backgroundTexture=resources.AddTexture(this.scene.backgroundTexturePath);
|
|
135
|
+
}
|
|
136
|
+
if(this.scene.diffuseCubemapPath&&this.scene.diffuseCubemapPath!="")
|
|
137
|
+
{
|
|
138
|
+
this.clientDynamicLighting.uid_diffuse_cubemap_texture_uid=resources.AddTexture(this.scene.diffuseCubemapPath);
|
|
139
|
+
}
|
|
140
|
+
if(this.scene.specularCubemapPath&&this.scene.specularCubemapPath!="")
|
|
141
|
+
{
|
|
142
|
+
this.clientDynamicLighting.uid_specular_cubemap_texture_uid=resources.AddTexture(this.scene.specularCubemapPath);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
this.SendCommand(this.setupCommand);
|
|
146
|
+
}
|
|
147
|
+
SendCommand(command){
|
|
148
|
+
let array=core.encodeToUint8Array(command);
|
|
149
|
+
this.signalingSend(array);
|
|
150
|
+
}
|
|
151
|
+
// We call StartStreaming once the SetupCommand has been acknowledged.
|
|
152
|
+
StartStreaming()
|
|
153
|
+
{
|
|
154
|
+
this.webRtcConnectionManager=WebRtcConnectionManager.getInstance();
|
|
155
|
+
// We make sure WebRTC has a connection for this client.
|
|
156
|
+
this.webRtcConnection = this.webRtcConnectionManager.createConnection(
|
|
157
|
+
this.clientID
|
|
158
|
+
,this.streamingConnectionStateChanged.bind(this)
|
|
159
|
+
,this.receivedMessageReliable.bind(this)
|
|
160
|
+
,this.receivedMessageUnreliable.bind(this));
|
|
161
|
+
//.then(
|
|
162
|
+
// function(value) {myDisplayer(value);},
|
|
163
|
+
// function(error) {myDisplayer(error);}
|
|
164
|
+
}
|
|
165
|
+
//! Cleanly shut down the WebRTC connection. This may be called when the client has signalled that it is
|
|
166
|
+
//! disconnecting, or when the server determines that the client is lost or needs to be booted.
|
|
167
|
+
StopStreaming()
|
|
168
|
+
{
|
|
169
|
+
this.webRtcConnectionManager.destroyConnection(this.clientID);
|
|
170
|
+
this.webRtcConnection=null;
|
|
171
|
+
}
|
|
172
|
+
// Generic message acknowledgement. Certain kinds of message are expected to be ack'ed.
|
|
173
|
+
ReceiveAcknowledgement(data)
|
|
174
|
+
{
|
|
175
|
+
if (data.byteLength!=message.AcknowledgementMessage.sizeof())
|
|
176
|
+
{
|
|
177
|
+
console.log("Client: Received malformed AcknowledgementMessage packet of length: ",data.length);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
var msg =new message.AcknowledgementMessage();
|
|
181
|
+
var bf =data;
|
|
182
|
+
var dataView =new DataView(data,0,data.length);
|
|
183
|
+
core.decodeFromDataView(msg,dataView,0);
|
|
184
|
+
if(msg.uint64_ackId==this.currentOriginState.ackId)
|
|
185
|
+
{
|
|
186
|
+
this.currentOriginState.acknowledged=true;
|
|
187
|
+
}
|
|
188
|
+
if(msg.uint64_ackId==this.currentLightingState.ackId)
|
|
189
|
+
{
|
|
190
|
+
this.currentLightingState.acknowledged=true;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
ReceiveNodePoses(data)
|
|
194
|
+
{
|
|
195
|
+
if (data.byteLength<message.NodePosesMessage.sizeof())
|
|
196
|
+
{
|
|
197
|
+
console.log("Client: Received malformed NodePosesMessage packet of length: ",data.length);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
var msg =new message.NodePosesMessage();
|
|
201
|
+
var dataView =new DataView(data,0,data.byteLength);
|
|
202
|
+
var byteOffset =0;
|
|
203
|
+
msg.messageType = dataView.getUint8(byteOffset, core.endian);
|
|
204
|
+
msg.timestamp = dataView.getBigUint64(byteOffset+1, core.endian);
|
|
205
|
+
byteOffset =msg.Pose_headPose.decodeOrientationPositionFromDataView(dataView, byteOffset+9);
|
|
206
|
+
msg.uint16_numPoses = dataView.getUint16(byteOffset, core.endian);
|
|
207
|
+
byteOffset+=2;
|
|
208
|
+
if (data.byteLength!=message.NodePosesMessage.sizeof()+msg.uint16_numPoses*28)
|
|
209
|
+
{
|
|
210
|
+
console.log("Client: Received malformed NodePosesMessage packet of length: ",data.length);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
msg.nodePoses = new Array(msg.uint16_numPoses);
|
|
214
|
+
for(let i=0; i <msg.uint16_numPoses; i++)
|
|
215
|
+
{
|
|
216
|
+
msg.nodePoses[i] = new NodePoseDynamic();
|
|
217
|
+
byteOffset = msg.nodePoses[i].decodeOrientationPositionFromDataView(dataView,byteOffset);
|
|
218
|
+
}
|
|
219
|
+
//console.log("Client: Received ", msg.uint16_numPoses, " node poses.");
|
|
220
|
+
this.ProcessNodePoses(msg.Pose_headPose,msg.uint16_numPoses, msg.nodePoses);
|
|
221
|
+
}
|
|
222
|
+
ProcessNodePoses(headPose,numPoses,nodePoses)
|
|
223
|
+
{
|
|
224
|
+
console.log("Client: ProcessNodePoses ", numPoses, " poses.");
|
|
225
|
+
}
|
|
226
|
+
UpdateStreaming()
|
|
227
|
+
{
|
|
228
|
+
if(!this.scene)
|
|
229
|
+
return;
|
|
230
|
+
if(!this.webRtcConnected)
|
|
231
|
+
return;
|
|
232
|
+
var timestamp=core.getTimestampUs();
|
|
233
|
+
// Establish which nodes the client should have, and their resources.
|
|
234
|
+
// Then: which resources we think it does not yet have. Send those.
|
|
235
|
+
var node_uids=this.scene.GetAllNodeUids();
|
|
236
|
+
for (let uid of node_uids)
|
|
237
|
+
{
|
|
238
|
+
this.geometryService.StreamNode(uid);
|
|
239
|
+
}
|
|
240
|
+
var nodes_to_stream_now_uids=this.geometryService.GetNodesToSend();
|
|
241
|
+
for (const uid of nodes_to_stream_now_uids)
|
|
242
|
+
{
|
|
243
|
+
this.SendNode(uid);
|
|
244
|
+
}
|
|
245
|
+
var mesh_uids=this.geometryService.GetMeshesToSend();
|
|
246
|
+
for (const uid of mesh_uids)
|
|
247
|
+
{
|
|
248
|
+
this.SendMesh(uid);
|
|
249
|
+
}
|
|
250
|
+
var canvases_to_send_now_uids=this.geometryService.GetCanvasesToSend();
|
|
251
|
+
for (const uid of canvases_to_send_now_uids)
|
|
252
|
+
{
|
|
253
|
+
this.SendCanvas(uid);
|
|
254
|
+
}
|
|
255
|
+
var font_atlases_to_send_now_uids=this.geometryService.GetFontAtlasesToSend();
|
|
256
|
+
for (const uid of font_atlases_to_send_now_uids)
|
|
257
|
+
{
|
|
258
|
+
this.SendFontAtlas(uid);
|
|
259
|
+
}
|
|
260
|
+
var textures_to_send_now_uids=this.geometryService.GetTexturesToSend();
|
|
261
|
+
for (const uid of textures_to_send_now_uids)
|
|
262
|
+
{
|
|
263
|
+
this.SendTexture(uid);
|
|
264
|
+
}
|
|
265
|
+
if(!this.currentOriginState.acknowledged)
|
|
266
|
+
this.SendOrigin();
|
|
267
|
+
if(!this.currentLightingState.acknowledged)
|
|
268
|
+
this.SendLighting();
|
|
269
|
+
}
|
|
270
|
+
SendOrigin()
|
|
271
|
+
{
|
|
272
|
+
let time_now_us=core.getTimestampUs();
|
|
273
|
+
let originAckWaitTimeUs=BigInt(3000000);// three seconds
|
|
274
|
+
if(this.setupCommand.startTimestamp_utc_unix_us==0)
|
|
275
|
+
return ;
|
|
276
|
+
// If we sent it, and haven't timed out waiting for ack...
|
|
277
|
+
if(this.currentOriginState.serverTimeSentUs!=BigInt(0)
|
|
278
|
+
&&(time_now_us-this.currentOriginState.serverTimeSentUs)<originAckWaitTimeUs)
|
|
279
|
+
{
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (!this.webRtcConnection)
|
|
283
|
+
{
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
if(this.currentOriginState.originClientHas==BigInt(0))
|
|
287
|
+
return;
|
|
288
|
+
this.currentOriginState.valid_counter++;
|
|
289
|
+
this.geometryService.SetOriginNode(this.currentOriginState.originClientHas);
|
|
290
|
+
var setp=new command.SetOriginNodeCommand();
|
|
291
|
+
setp.uint64_ackId=this.next_ack_id++;
|
|
292
|
+
setp.uint64_originNodeUid=this.currentOriginState.originClientHas;
|
|
293
|
+
setp.uint64_validCounter = this.currentOriginState.valid_counter;
|
|
294
|
+
|
|
295
|
+
// This is now the valid origin.
|
|
296
|
+
this.currentOriginState.sent=true;
|
|
297
|
+
this.currentOriginState.ackId=setp.uint64_ackId;
|
|
298
|
+
this.currentOriginState.acknowledged=false;
|
|
299
|
+
this.currentOriginState.serverTimeSentUs=core.getTimestampUs();
|
|
300
|
+
this.SendCommand(setp);
|
|
301
|
+
}
|
|
302
|
+
SendLighting()
|
|
303
|
+
{
|
|
304
|
+
let time_now_us=core.getTimestampUs();
|
|
305
|
+
let ackWaitTimeUs=BigInt(3000000);// three seconds
|
|
306
|
+
if(this.setupCommand.startTimestamp_utc_unix_us==0)
|
|
307
|
+
return;
|
|
308
|
+
// If we sent it, and haven't timed out waiting for ack...
|
|
309
|
+
if(this.currentLightingState.serverTimeSentUs!=BigInt(0)
|
|
310
|
+
&&(time_now_us-this.currentLightingState.serverTimeSentUs)<ackWaitTimeUs)
|
|
311
|
+
{
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
if (!this.webRtcConnection)
|
|
315
|
+
{
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
var setl =new command.SetLightingCommand();
|
|
320
|
+
setl.uint64_ackId =this.next_ack_id++;
|
|
321
|
+
setl.ClientDynamicLighting_clientDynamicLighting = this.clientDynamicLighting;
|
|
322
|
+
|
|
323
|
+
// This is now the valid origin.
|
|
324
|
+
this.currentLightingState.ackId=setl.uint64_ackId;
|
|
325
|
+
this.currentLightingState.acknowledged=false;
|
|
326
|
+
this.currentLightingState.serverTimeSentUs=core.getTimestampUs();
|
|
327
|
+
this.SendCommand(setl);
|
|
328
|
+
}
|
|
329
|
+
SendNode(uid)
|
|
330
|
+
{
|
|
331
|
+
var node=this.scene.GetNode(uid);
|
|
332
|
+
const MAX_NODE_SIZE=500;
|
|
333
|
+
const buffer = new ArrayBuffer(MAX_NODE_SIZE);
|
|
334
|
+
const nodeSize=node_encoder.encodeNode(node,buffer);
|
|
335
|
+
this.geometryService.EncodedResource(uid);
|
|
336
|
+
const view2 = new DataView(buffer, 0, nodeSize);
|
|
337
|
+
console.log("Sending node "+uid+" "+node.name+" to Client "+this.clientID+", size: "+nodeSize+" bytes");
|
|
338
|
+
if(!this.webRtcConnection)
|
|
339
|
+
{
|
|
340
|
+
console.error("this.webRtcConnection is null");
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if(!this.webRtcConnection.sendGeometry)
|
|
344
|
+
{
|
|
345
|
+
console.error("this.webRtcConnection.sendGeometry is null");
|
|
346
|
+
console.log(JSON.stringify(this.webRtcConnection));
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
this.webRtcConnection.sendGeometry(view2);
|
|
350
|
+
}
|
|
351
|
+
SendGenericResource(uid)
|
|
352
|
+
{
|
|
353
|
+
var resource=resources.GetResourceFromUid(uid);
|
|
354
|
+
if(!resource)
|
|
355
|
+
{
|
|
356
|
+
console.warn("No resource of uid ",uid," was found.")
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const MAX_BUFFER_SIZE=resource.encodedSize();;
|
|
360
|
+
const buffer = new ArrayBuffer(MAX_BUFFER_SIZE);
|
|
361
|
+
const resourceSize=resource_encoder.EncodeResource(resource,buffer);
|
|
362
|
+
this.geometryService.EncodedResource(uid);
|
|
363
|
+
const view2 = new DataView(buffer, 0, resourceSize);
|
|
364
|
+
console.log("Sending resource "+uid+" "+resource.url+" to Client "+this.clientID+", size: "+resourceSize+" bytes");
|
|
365
|
+
this.webRtcConnection.sendGeometry(view2);
|
|
366
|
+
}
|
|
367
|
+
SendMesh(uid)
|
|
368
|
+
{
|
|
369
|
+
this.SendGenericResource(uid);
|
|
370
|
+
}
|
|
371
|
+
SendTexture(uid)
|
|
372
|
+
{
|
|
373
|
+
this.SendGenericResource(uid);
|
|
374
|
+
}
|
|
375
|
+
SendCanvas(uid)
|
|
376
|
+
{
|
|
377
|
+
this.SendGenericResource(uid);
|
|
378
|
+
}
|
|
379
|
+
SendFontAtlas(uid)
|
|
380
|
+
{
|
|
381
|
+
this.SendGenericResource(uid);
|
|
382
|
+
}
|
|
383
|
+
receiveHandshake(data)
|
|
384
|
+
{
|
|
385
|
+
if(data.length<message.HandshakeMessage.sizeof()) {
|
|
386
|
+
// NOTE: we use log() here rather than warn() or error() because this is a problem with
|
|
387
|
+
// the message we were SENT, not with the local server code.
|
|
388
|
+
console.log("Binary message from "+this.clientID+" is too small to be a Handshake: "+data.length+"<"+message.HandshakeMessage.sizeof());
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
var handshakeMessage=new message.HandshakeMessage();
|
|
392
|
+
core.decodeFromUint8Array(handshakeMessage,data);
|
|
393
|
+
const excess =data.length-message.HandshakeMessage.sizeof();
|
|
394
|
+
const numReceived =excess/core.UID_SIZE;
|
|
395
|
+
if(numReceived!=handshakeMessage.uint64_resourceCount) {
|
|
396
|
+
console.log("Handshake claims to have "<<handshakeMessage.uint64_resourceCount<<" resources but has only enough data for "+numReceived);
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
var offset =message.HandshakeMessage.sizeof();
|
|
400
|
+
var dataView =new DataView(data.buffer,offset,data.offset,data.length);
|
|
401
|
+
for(let i=0;i<this.handshakeMessage.resourceCount;i++ ) {
|
|
402
|
+
var uid=dataView.getBigUint64(offset,core.endian);
|
|
403
|
+
this.geometryService.ConfirmResource(uid);
|
|
404
|
+
offset+=core.UID_SIZE;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
var acknowledgeHandshakeCommand=new command.AcknowledgeHandshakeCommand;
|
|
408
|
+
this.SendCommand(acknowledgeHandshakeCommand);
|
|
409
|
+
// And now, setup is complete. On the next geometry update, we can send nodes/resources.
|
|
410
|
+
this.StartStreaming();
|
|
411
|
+
}
|
|
412
|
+
receiveReliableBinaryMessage(data){
|
|
413
|
+
const messageType=data[0];
|
|
414
|
+
switch(messageType){
|
|
415
|
+
case message.MessagePayloadType.Handshake:
|
|
416
|
+
this.receiveHandshake(data);
|
|
417
|
+
return;
|
|
418
|
+
case message.MessagePayloadType.Acknowledgement:
|
|
419
|
+
this.ReceiveAcknowledgement(data);
|
|
420
|
+
return;
|
|
421
|
+
default:
|
|
422
|
+
break;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
SetScene(sc){
|
|
426
|
+
this.scene=sc;
|
|
427
|
+
this.geometryService.SetScene(sc);
|
|
428
|
+
}
|
|
429
|
+
setOrigin(origin_node_uid)
|
|
430
|
+
{
|
|
431
|
+
if(origin_node_uid==0)
|
|
432
|
+
return;
|
|
433
|
+
if(this.currentOriginState.originClientHas==origin_node_uid)
|
|
434
|
+
return;
|
|
435
|
+
// It's a different origin. So we reset the time sent.
|
|
436
|
+
this.currentOriginState.serverTimeSentUs=BigInt(0);
|
|
437
|
+
this.currentOriginState.originClientHas=origin_node_uid;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
module.exports = { Client };
|