teleportxr 1.0.0
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/.vscode/launch.json +18 -0
- package/.vscode/settings.json +12 -0
- package/LICENSE +21 -0
- package/LICENSES/Apache-2.0.txt +73 -0
- package/LICENSES/BSD-3-Clause.txt +11 -0
- package/LICENSES/BSL-1.0.txt +7 -0
- package/LICENSES/CC-BY-4.0.txt +156 -0
- package/LICENSES/CC0-1.0.txt +121 -0
- package/LICENSES/MIT.txt +9 -0
- package/README.md +6 -0
- package/assets/scene.json +8 -0
- package/client/client.js +305 -0
- package/client/client_manager.js +114 -0
- package/client/geometry_service.js +422 -0
- package/connections/connection.js +0 -0
- package/connections/connectionmanager.js +0 -0
- package/connections/webrtcconnection.js +384 -0
- package/connections/webrtcconnectionmanager.js +86 -0
- package/core/core.js +447 -0
- package/index.js +13 -0
- package/package.json +36 -0
- package/protocol/command.js +127 -0
- package/protocol/encoders/node_encoder.js +28 -0
- package/protocol/encoders/resource_encoder.js +28 -0
- package/protocol/message.js +145 -0
- package/scene/material.js +105 -0
- package/scene/node.js +255 -0
- package/scene/resources.js +62 -0
- package/scene/scene.js +83 -0
- package/server.js +35 -0
- package/signaling.js +255 -0
- package/temp.js +1 -0
- package/test.json +2 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const core= require("../core/core.js");
|
|
3
|
+
const command= require("./command");
|
|
4
|
+
|
|
5
|
+
function decodeFromDataView(obj,dataView,byteOffset){
|
|
6
|
+
for (let key of Object.keys(obj)) {
|
|
7
|
+
let first_underscore=key.search('_');
|
|
8
|
+
var name=key.substring(first_underscore+1,key.end);
|
|
9
|
+
var type=key.substring(0,first_underscore);
|
|
10
|
+
var [sz,tp]=SizeOfType(type);
|
|
11
|
+
if(tp=="uint8")
|
|
12
|
+
{
|
|
13
|
+
var value=dataView.getUint8(byteOffset);
|
|
14
|
+
Object.entries[key]=value;
|
|
15
|
+
}
|
|
16
|
+
else if(tp=="int8")
|
|
17
|
+
{
|
|
18
|
+
var value=dataView.getInt8(byteOffset);
|
|
19
|
+
Object.entries[key]=value;
|
|
20
|
+
}
|
|
21
|
+
else if(tp=="uint32")
|
|
22
|
+
{
|
|
23
|
+
var value=dataView.getUint32(byteOffset);
|
|
24
|
+
Object.entries[key]=value;
|
|
25
|
+
}
|
|
26
|
+
else if(tp=="int32")
|
|
27
|
+
{
|
|
28
|
+
var value=dataView.getInt32(byteOffset);
|
|
29
|
+
Object.entries[key]=value;
|
|
30
|
+
}
|
|
31
|
+
else if(tp=="float32")
|
|
32
|
+
{
|
|
33
|
+
var value=dataView.getFloat32(byteOffset);
|
|
34
|
+
Object.entries[key]=value;
|
|
35
|
+
}
|
|
36
|
+
else if(tp=="int64")
|
|
37
|
+
{
|
|
38
|
+
var value=dataView.getBigInt64(byteOffset);
|
|
39
|
+
Object.entries[key]=value;
|
|
40
|
+
}
|
|
41
|
+
else if(tp=="uint64")
|
|
42
|
+
{
|
|
43
|
+
var value=dataView.get4BigUint6(byteOffset);
|
|
44
|
+
Object.entries[key]=value;
|
|
45
|
+
}
|
|
46
|
+
else if(tp=="struct")
|
|
47
|
+
{
|
|
48
|
+
sz=decodeFromDataView(value,dataView,byteOffset)-byteOffset;
|
|
49
|
+
}
|
|
50
|
+
byteOffset+=sz;
|
|
51
|
+
console.log(byteOffset+": "+sz+" bytes\t\t"+tp+" "+name+" "+value.toString());
|
|
52
|
+
}
|
|
53
|
+
return byteOffset;
|
|
54
|
+
}
|
|
55
|
+
//! The payload type, or how to interpret the server's message.
|
|
56
|
+
const MessagePayloadType =
|
|
57
|
+
{
|
|
58
|
+
Invalid:0,
|
|
59
|
+
Handshake:1,
|
|
60
|
+
NodeStatus:2,
|
|
61
|
+
ReceivedResources:3,
|
|
62
|
+
ControllerPoses:4,
|
|
63
|
+
ResourceLost:5, //! Inform the server that client "lost" a previously confirmed resource, e.g. due to some bug or error. Should *rarely* be used.
|
|
64
|
+
InputStates:6,
|
|
65
|
+
InputEvents:7,
|
|
66
|
+
DisplayInfo:8,
|
|
67
|
+
KeyframeRequest:9,
|
|
68
|
+
PongForLatency:10,
|
|
69
|
+
OrthogonalAcknowledgement:11,
|
|
70
|
+
Acknowledgement:12
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/// A message sent from client to server: may be sent on a reliable or unreliable channel, including via the signaling protocol.
|
|
74
|
+
class Message{
|
|
75
|
+
constructor(){
|
|
76
|
+
this.MessagePayloadType_messagePayloadType=MessagePayloadType.Invalid;
|
|
77
|
+
this.int64_timestamp=BigInt(0);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
class HandshakeMessage extends Message
|
|
82
|
+
{
|
|
83
|
+
constructor(){
|
|
84
|
+
super();
|
|
85
|
+
// type=1 byte
|
|
86
|
+
this.MessagePayloadType_messagePayloadType=MessagePayloadType.Handshake;
|
|
87
|
+
this.int64_timestamp=BigInt(0);
|
|
88
|
+
this.DisplayInfo_startDisplayInfo = new core.DisplayInfo();
|
|
89
|
+
this.float32_MetresPerUnit = 1.0;
|
|
90
|
+
this.float32_FOV = 90.0;
|
|
91
|
+
this.uint32_udpBufferSize = 0; // In kilobytes.
|
|
92
|
+
this.uint32_maxBandwidthKpS = 0; // In kilobytes per second
|
|
93
|
+
this.AxesStandard_axesStandard = core.AxesStandard.NotInitialized;
|
|
94
|
+
this.uint8_framerate = 0; // In hertz
|
|
95
|
+
this.bool_isVR = true;
|
|
96
|
+
this.uint64_resourceCount = 0; //Count of resources the client has, which are appended to the handshake.
|
|
97
|
+
this.uint32_maxLightsSupported = 0;
|
|
98
|
+
this.int32_minimumPriority = 0; // The lowest priority object this client will render, meshes with lower priority need not be sent.
|
|
99
|
+
this.RenderingFeatures_renderingFeatures=new core.RenderingFeatures();
|
|
100
|
+
}
|
|
101
|
+
static sizeof(){
|
|
102
|
+
return 58;
|
|
103
|
+
}
|
|
104
|
+
size(){
|
|
105
|
+
return HandshakeMessage.sizeof();
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
class ReceivedResourcesMessage extends Message
|
|
110
|
+
{
|
|
111
|
+
constructor(){
|
|
112
|
+
super();
|
|
113
|
+
// type=1 byte
|
|
114
|
+
this.MessagePayloadType_messagePayloadType=MessagePayloadType.ReceivedResources;
|
|
115
|
+
// timestamp 8 bytes.
|
|
116
|
+
// count 8 bytes
|
|
117
|
+
this.uint64_receivedResourcesCount=BigInt(0);
|
|
118
|
+
// = 17 + 8 * num resources.
|
|
119
|
+
}
|
|
120
|
+
static sizeof(){
|
|
121
|
+
return 17;
|
|
122
|
+
}
|
|
123
|
+
size(){
|
|
124
|
+
return ReceivedResourcesMessage.sizeof();
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
class AcknowledgementMessage extends Message
|
|
128
|
+
{
|
|
129
|
+
constructor(){
|
|
130
|
+
super();
|
|
131
|
+
// type=1 byte
|
|
132
|
+
this.MessagePayloadType_messagePayloadType=MessagePayloadType.Acknowledgement;
|
|
133
|
+
// timestamp 8 bytes.
|
|
134
|
+
// count 8 bytes
|
|
135
|
+
this.uint64_ackId=BigInt(0);
|
|
136
|
+
// = 17 bytes
|
|
137
|
+
}
|
|
138
|
+
static sizeof(){
|
|
139
|
+
return 17;
|
|
140
|
+
}
|
|
141
|
+
size() {
|
|
142
|
+
return AcknowledgementMessage.sizeof();
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
module.exports= {Message,MessagePayloadType,HandshakeMessage,ReceivedResourcesMessage,AcknowledgementMessage};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const core= require('../core/core.js');
|
|
3
|
+
const resources= require('./resources.js');
|
|
4
|
+
|
|
5
|
+
const MaterialMode =
|
|
6
|
+
{
|
|
7
|
+
UNKNOWNMODE:0,
|
|
8
|
+
OPAQUE_MATERIAL:1,
|
|
9
|
+
TRANSPARENT_MATERIAL:2
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
class MaterialTexture
|
|
13
|
+
{
|
|
14
|
+
constructor(uid=0,texCoord=0,tile_x=1.0,tile_y=1.0)
|
|
15
|
+
{
|
|
16
|
+
this.textureUid = uid;
|
|
17
|
+
this.texCoord = texCoord;
|
|
18
|
+
this.tiling = {x:tile_x, y:tile_y};
|
|
19
|
+
}
|
|
20
|
+
encodeIntoDataView(dataView,byteOffset) {
|
|
21
|
+
byteOffset=core.put_uint64(dataView,byteOffset,this.textureUid);
|
|
22
|
+
byteOffset=core.put_uint8(dataView,byteOffset,this.texCoord);
|
|
23
|
+
byteOffset=core.put_float32(dataView,byteOffset,this.tiling.x);
|
|
24
|
+
byteOffset=core.put_float32(dataView,byteOffset,this.tiling.y);
|
|
25
|
+
return byteOffset;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
class Material
|
|
30
|
+
{
|
|
31
|
+
constructor(uid,name="")
|
|
32
|
+
{
|
|
33
|
+
this.uid=uid;
|
|
34
|
+
this.name=name;
|
|
35
|
+
|
|
36
|
+
this.materialMode = MaterialMode.OPAQUE_MATERIAL;
|
|
37
|
+
this.baseColorTexture=new MaterialTexture();
|
|
38
|
+
this.baseColorFactor = {x:1.0, y:1.0, z: 1.0, w: 1.0};
|
|
39
|
+
this.metallicRoughnessTexture=new MaterialTexture();
|
|
40
|
+
this.metallicFactor = 0.0;
|
|
41
|
+
this.roughnessMultiplier = 1.0;
|
|
42
|
+
this.roughnessOffset = 0.0;
|
|
43
|
+
|
|
44
|
+
this.normalTexture=new MaterialTexture();
|
|
45
|
+
this.normalTexture.scale = NextFloat;
|
|
46
|
+
|
|
47
|
+
this.occlusionTexture=new MaterialTexture();
|
|
48
|
+
|
|
49
|
+
this.emissiveTexture=new MaterialTexture();
|
|
50
|
+
this.emissiveFactor={x: 0.0, y: 0.0, z:0.0};
|
|
51
|
+
|
|
52
|
+
this.doubleSided = false;
|
|
53
|
+
this.lightmapTexCoordIndex = 0;
|
|
54
|
+
|
|
55
|
+
this.extensions = [];
|
|
56
|
+
}
|
|
57
|
+
encodeIntoDataView(dataView,byteOffset) {
|
|
58
|
+
|
|
59
|
+
byteOffset=core.put_uint8(dataView,byteOffset,core.GeometryPayloadType.Material);
|
|
60
|
+
byteOffset=core.put_uint64(dataView,byteOffset,this.uid);
|
|
61
|
+
byteOffset=core.put_string(dataView,byteOffset,this.name);
|
|
62
|
+
|
|
63
|
+
byteOffset=core.put_uint8(dataView,byteOffset,this.materialMode);
|
|
64
|
+
|
|
65
|
+
byteOffset=this.baseColorTexture.encodeIntoDataView(dataView,byteOffset);
|
|
66
|
+
byteOffset=core.put_vec4(dataView,byteOffset,this.baseColorFactor);
|
|
67
|
+
|
|
68
|
+
byteOffset=this.metallicRoughnessTexture.encodeIntoDataView(dataView,byteOffset);
|
|
69
|
+
|
|
70
|
+
byteOffset=core.put_float32(dataView,byteOffset,this.metallicFactor);
|
|
71
|
+
byteOffset=core.put_float32(dataView,byteOffset,this.roughnessMultiplier);
|
|
72
|
+
byteOffset=core.put_float32(dataView,byteOffset,this.roughnessOffset);
|
|
73
|
+
|
|
74
|
+
byteOffset=this.normalTexture.encodeIntoDataView(dataView,byteOffset);
|
|
75
|
+
// TODO make this depend on (renderingFeatures.normals)
|
|
76
|
+
|
|
77
|
+
byteOffset=core.put_float32(dataView,byteOffset,1.0);
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
byteOffset=this.occlusionTexture.encodeIntoDataView(dataView,byteOffset);
|
|
81
|
+
|
|
82
|
+
byteOffset=core.put_float32(dataView,byteOffset,1.0);
|
|
83
|
+
|
|
84
|
+
byteOffset=this.emissiveTexture.encodeIntoDataView(dataView,byteOffset);
|
|
85
|
+
|
|
86
|
+
byteOffset=core.put_vec3(dataView,byteOffset,this.emissiveFactor);
|
|
87
|
+
|
|
88
|
+
byteOffset=core.put_uint8(dataView,byteOffset,this.doubleSided);
|
|
89
|
+
byteOffset=core.put_uint8(dataView,byteOffset,this.lightmapTexCoordIndex);
|
|
90
|
+
|
|
91
|
+
byteOffset=core.put_uint64(dataView,byteOffset,BigInt(0));
|
|
92
|
+
}
|
|
93
|
+
encodeToUint8Array(){
|
|
94
|
+
var array=new Uint8Array(this.size());
|
|
95
|
+
var dataView=new DataView(array.buffer);
|
|
96
|
+
this.encodeIntoDataView(dataView);
|
|
97
|
+
return array;
|
|
98
|
+
}
|
|
99
|
+
/*static sizeof() {
|
|
100
|
+
return 8+24+Pose.size+8;
|
|
101
|
+
}
|
|
102
|
+
size() {
|
|
103
|
+
return Node.sizeof();
|
|
104
|
+
}*/
|
|
105
|
+
};
|
package/scene/node.js
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const core= require('../core/core.js');
|
|
3
|
+
const resources= require('./resources.js');
|
|
4
|
+
//! The payload type, or how to interpret the server's message.
|
|
5
|
+
const NodeDataType =
|
|
6
|
+
{
|
|
7
|
+
Invalid:0,
|
|
8
|
+
None:1,
|
|
9
|
+
Mesh:2,
|
|
10
|
+
Light:3,
|
|
11
|
+
TextCanvas:4,
|
|
12
|
+
SubScene:5,
|
|
13
|
+
Skeleton:6,
|
|
14
|
+
Link:7,
|
|
15
|
+
Script:8
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
class Pose
|
|
19
|
+
{
|
|
20
|
+
constructor()
|
|
21
|
+
{
|
|
22
|
+
this.orientation = { x:0.0, y:0.0, z:0.0, w:1.0 };
|
|
23
|
+
this.position = { x:0.0, y:0.0, z:0.0 };
|
|
24
|
+
this.scale = { x:1.0, y:1.0, z:1.0 };
|
|
25
|
+
}
|
|
26
|
+
static sizeof(){
|
|
27
|
+
return 4*4+(3*4)+(3*4);
|
|
28
|
+
}
|
|
29
|
+
size(){
|
|
30
|
+
return Pose.sizeof();
|
|
31
|
+
}
|
|
32
|
+
encodeIntoDataView(dataView,byteOffset) {
|
|
33
|
+
dataView.setFloat32(byteOffset+0,this.position.x,core.endian);
|
|
34
|
+
dataView.setFloat32(byteOffset+4,this.position.y,core.endian);
|
|
35
|
+
dataView.setFloat32(byteOffset+8,this.position.z,core.endian);
|
|
36
|
+
dataView.setFloat32(byteOffset+12,this.orientation.x,core.endian);
|
|
37
|
+
dataView.setFloat32(byteOffset+16,this.orientation.y,core.endian);
|
|
38
|
+
dataView.setFloat32(byteOffset+20,this.orientation.z,core.endian);
|
|
39
|
+
dataView.setFloat32(byteOffset+24,this.orientation.w,core.endian);
|
|
40
|
+
dataView.setFloat32(byteOffset+28,this.scale.x,core.endian);
|
|
41
|
+
dataView.setFloat32(byteOffset+32,this.scale.y,core.endian);
|
|
42
|
+
dataView.setFloat32(byteOffset+36,this.scale.z,core.endian);
|
|
43
|
+
return byteOffset+40;
|
|
44
|
+
}
|
|
45
|
+
encodeToUint8Array(){
|
|
46
|
+
var array=new Uint8Array(this.size());
|
|
47
|
+
var dataView=new DataView(array.buffer);
|
|
48
|
+
this.encodeIntoDataView(dataView);
|
|
49
|
+
return array;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
class PoseDynamic
|
|
53
|
+
{
|
|
54
|
+
constructor(){
|
|
55
|
+
this.pose=new Pose();
|
|
56
|
+
this.velocity={x:0.0, y:0.0, z:0.0 };
|
|
57
|
+
this.angularVelocity={x:0.0, y:0.0, z:0.0 };
|
|
58
|
+
}
|
|
59
|
+
static sizeof(){
|
|
60
|
+
return Pose.sizeof()+(3*4)+(3*4);
|
|
61
|
+
}
|
|
62
|
+
size(){
|
|
63
|
+
return PoseDynamic.sizeof();
|
|
64
|
+
}
|
|
65
|
+
encodeIntoDataView(dataView,byteOffset) {
|
|
66
|
+
dataView.setFloat32(byteOffset+0,this.position.x,core.endian);
|
|
67
|
+
dataView.setFloat32(byteOffset+4,this.position.y,core.endian);
|
|
68
|
+
dataView.setFloat32(byteOffset+8,this.position.z,core.endian);
|
|
69
|
+
dataView.setFloat32(byteOffset+12,this.orientation.x,core.endian);
|
|
70
|
+
dataView.setFloat32(byteOffset+16,this.orientation.y,core.endian);
|
|
71
|
+
dataView.setFloat32(byteOffset+20,this.orientation.z,core.endian);
|
|
72
|
+
dataView.setFloat32(byteOffset+24,this.orientation.w,core.endian);
|
|
73
|
+
dataView.setFloat32(byteOffset+28,this.scale.x,core.endian);
|
|
74
|
+
dataView.setFloat32(byteOffset+32,this.scale.y,core.endian);
|
|
75
|
+
dataView.setFloat32(byteOffset+36,this.scale.z,core.endian);
|
|
76
|
+
|
|
77
|
+
dataView.setFloat32(byteOffset+40,this.velocity.x,core.endian);
|
|
78
|
+
dataView.setFloat32(byteOffset+44,this.velocity.y,core.endian);
|
|
79
|
+
dataView.setFloat32(byteOffset+48,this.velocity.z,core.endian);
|
|
80
|
+
dataView.setFloat32(byteOffset+52,this.angularVelocity.x,core.endian);
|
|
81
|
+
dataView.setFloat32(byteOffset+56,this.angularVelocity.y,core.endian);
|
|
82
|
+
dataView.setFloat32(byteOffset+60,this.angularVelocity.z,core.endian);
|
|
83
|
+
return byteOffset+64;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class RenderState
|
|
89
|
+
{
|
|
90
|
+
constructor(){
|
|
91
|
+
this.lightmapScaleOffset= { x:1.0, y:1.0, z:0.0, w:0.0 };
|
|
92
|
+
this.globalIlluminationUid=0;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
class Component {
|
|
97
|
+
constructor()
|
|
98
|
+
{
|
|
99
|
+
this.uid=0;
|
|
100
|
+
this.data_uid=0;
|
|
101
|
+
}
|
|
102
|
+
getType(){
|
|
103
|
+
return NodeDataType.Invalid;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
class MeshComponent extends Component
|
|
108
|
+
{
|
|
109
|
+
constructor()
|
|
110
|
+
{
|
|
111
|
+
super();
|
|
112
|
+
this.skeletonNodeID=0;
|
|
113
|
+
this.renderState = new RenderState();
|
|
114
|
+
this.meshUrl="";
|
|
115
|
+
}
|
|
116
|
+
getType() {
|
|
117
|
+
return NodeDataType.Mesh;
|
|
118
|
+
}
|
|
119
|
+
encodeIntoDataView(dataView,byteOffset) {
|
|
120
|
+
byteOffset=core.put_uint8(dataView,byteOffset,NodeDataType.Mesh);
|
|
121
|
+
|
|
122
|
+
var resuid=resources.GetResourceUidFromUrl(core.GeometryPayloadType.MeshPointer,this.meshUrl);
|
|
123
|
+
byteOffset=core.put_uint64(dataView,byteOffset,resuid);
|
|
124
|
+
|
|
125
|
+
byteOffset=core.put_uint64(dataView,byteOffset,this.skeletonNodeID);
|
|
126
|
+
|
|
127
|
+
var num_joint_indices=0;
|
|
128
|
+
byteOffset=core.put_uint16(dataView,byteOffset,num_joint_indices);
|
|
129
|
+
for (var i =0;i<num_joint_indices;i++)
|
|
130
|
+
{
|
|
131
|
+
var index=this.joint_indices[i];
|
|
132
|
+
byteOffset=put_int16(dataView,byteOffset,index);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
var num_animations=0;
|
|
136
|
+
byteOffset=core.put_uint16(dataView,byteOffset,num_animations);
|
|
137
|
+
for (var i =0;i<num_animations;i++)
|
|
138
|
+
{
|
|
139
|
+
byteOffset=core.put_uint64(dataView,byteOffset,this.animations[i]);
|
|
140
|
+
}
|
|
141
|
+
// If the node's priority is less than the *client's* minimum, we don't want
|
|
142
|
+
// to send its mesh.
|
|
143
|
+
|
|
144
|
+
var num_materials=0;
|
|
145
|
+
byteOffset=core.put_uint16(dataView,byteOffset,num_materials);
|
|
146
|
+
for (var i =0;i<num_materials;i++)
|
|
147
|
+
{
|
|
148
|
+
byteOffset=core.put_uint64(dataView,byteOffset,this.materials[i]);
|
|
149
|
+
}
|
|
150
|
+
byteOffset=core.put_vec4(dataView,byteOffset,this.renderState.lightmapScaleOffset);
|
|
151
|
+
byteOffset=core.put_uint64(dataView,byteOffset,this.renderState.globalIlluminationUid);
|
|
152
|
+
|
|
153
|
+
return byteOffset;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
class SkeletonComponent extends Component
|
|
158
|
+
{
|
|
159
|
+
constructor()
|
|
160
|
+
{
|
|
161
|
+
super();
|
|
162
|
+
}
|
|
163
|
+
getType(){
|
|
164
|
+
return NodeDataType.Skeleton;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
class Node
|
|
169
|
+
{
|
|
170
|
+
constructor(uid,name="")
|
|
171
|
+
{
|
|
172
|
+
this.uid=uid;
|
|
173
|
+
this.name=name;
|
|
174
|
+
this.pose=new Pose();
|
|
175
|
+
this.parent_uid=0;
|
|
176
|
+
|
|
177
|
+
this.holder_client_id=0;
|
|
178
|
+
this.stationary=true;
|
|
179
|
+
|
|
180
|
+
this.priority=0;
|
|
181
|
+
|
|
182
|
+
this.components = [];
|
|
183
|
+
}
|
|
184
|
+
static sizeof() {
|
|
185
|
+
return 8+24+Pose.size+8;
|
|
186
|
+
}
|
|
187
|
+
size() {
|
|
188
|
+
return Node.sizeof();
|
|
189
|
+
}
|
|
190
|
+
setMeshComponent(mesh_url) {
|
|
191
|
+
this.components.forEach(component => {
|
|
192
|
+
if(component.getType()==NodeDataType.Mesh) {
|
|
193
|
+
component.meshUrl=mesh_url;
|
|
194
|
+
component.data_uid=resources.GetResourceUidFromUrl(core.GeometryPayloadType.MeshPointer,mesh_url);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
var m=new MeshComponent();
|
|
199
|
+
m.meshUrl=mesh_url;
|
|
200
|
+
m.data_uid=resources.GetResourceUidFromUrl(core.GeometryPayloadType.MeshPointer,mesh_url);
|
|
201
|
+
this.components.push(m);
|
|
202
|
+
}
|
|
203
|
+
encodeIntoDataView(dataView,byteOffset) {
|
|
204
|
+
byteOffset=core.put_uint8(dataView,byteOffset,core.GeometryPayloadType.Node);
|
|
205
|
+
|
|
206
|
+
byteOffset=core.put_uint64(dataView,byteOffset,this.uid);
|
|
207
|
+
byteOffset=core.put_string(dataView,byteOffset,this.name);
|
|
208
|
+
var clientsidePose=this.pose;
|
|
209
|
+
//avs::ConvertTransform(serverSettings.serverAxesStandard, geometryStreamingService.getClientAxesStandard(), localTransform);
|
|
210
|
+
byteOffset=clientsidePose.encodeIntoDataView(dataView,byteOffset);
|
|
211
|
+
//put(node.localTransform);
|
|
212
|
+
byteOffset=core.put_uint8(dataView,byteOffset,this.stationary);
|
|
213
|
+
byteOffset=core.put_uint64(dataView,byteOffset,this.holder_client_id);
|
|
214
|
+
byteOffset=core.put_int32(dataView,byteOffset,this.priority);
|
|
215
|
+
byteOffset=core.put_uint64(dataView,byteOffset,this.parent_uid);
|
|
216
|
+
|
|
217
|
+
// Data components. Let's say 8 bits for number of components.
|
|
218
|
+
byteOffset=core.put_uint8(dataView,byteOffset,this.components.length);
|
|
219
|
+
for(var i=0;i<this.components.length;i++) {
|
|
220
|
+
byteOffset=this.components[i].encodeIntoDataView(dataView,byteOffset);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return byteOffset;
|
|
224
|
+
/*
|
|
225
|
+
if (this.data_type ==NodeDataType.Light)
|
|
226
|
+
{
|
|
227
|
+
put(this.lightColour);
|
|
228
|
+
put(this.lightRadius);
|
|
229
|
+
put(this.lightRange);
|
|
230
|
+
vec3 lightDirection = this.lightDirection;
|
|
231
|
+
avs::ConvertPosition(serverSettings.serverAxesStandard, geometryStreamingService.getClientAxesStandard(), lightDirection);
|
|
232
|
+
put(lightDirection);
|
|
233
|
+
put(this.lightType);
|
|
234
|
+
}
|
|
235
|
+
if (this.data_type == avs::NodeDataType::TextCanvas)
|
|
236
|
+
{
|
|
237
|
+
// nothing this-specific to add at present.
|
|
238
|
+
}
|
|
239
|
+
if (this.data_type == avs::NodeDataType::Skeleton)
|
|
240
|
+
{
|
|
241
|
+
}
|
|
242
|
+
if (this.data_type == avs::NodeDataType::Link)
|
|
243
|
+
{
|
|
244
|
+
size_t urlLength = this.url.length();
|
|
245
|
+
put(urlLength);
|
|
246
|
+
put((uint8_t *)this.url.data(), urlLength);
|
|
247
|
+
size_t queryLength = this.query_url.length();
|
|
248
|
+
put(queryLength);
|
|
249
|
+
put((uint8_t *)this.query_url.data(), queryLength);
|
|
250
|
+
|
|
251
|
+
}*/
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
module.exports = {NodeDataType,Pose,PoseDynamic,Node };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const core= require('../core/core.js');
|
|
3
|
+
|
|
4
|
+
/// Each resource has a url. If it's a local URL, the resource
|
|
5
|
+
/// is stored here on the server. If not, it's a remotely stored resource,
|
|
6
|
+
/// accessed by https.
|
|
7
|
+
class Resource
|
|
8
|
+
{
|
|
9
|
+
//! One trackedResources shared acrosss all clients.
|
|
10
|
+
static resourcesByUid=new Map();
|
|
11
|
+
static pathToUid=new Map();
|
|
12
|
+
constructor(type,uid,url)
|
|
13
|
+
{
|
|
14
|
+
this.uid=uid;
|
|
15
|
+
this.url=url;
|
|
16
|
+
this.type=type;
|
|
17
|
+
}
|
|
18
|
+
encodeIntoDataView(dataView,byteOffset) {
|
|
19
|
+
byteOffset=core.put_uint8(dataView,byteOffset,this.type);
|
|
20
|
+
byteOffset=core.put_uint64(dataView,byteOffset,this.uid);
|
|
21
|
+
byteOffset=core.put_string(dataView,byteOffset,this.url);
|
|
22
|
+
return byteOffset;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function GetResourceUidFromUrl(type,url)
|
|
27
|
+
{
|
|
28
|
+
if(Resource.pathToUid.has(url))
|
|
29
|
+
{
|
|
30
|
+
var uid=Resource.pathToUid.get(url);
|
|
31
|
+
return uid;
|
|
32
|
+
}
|
|
33
|
+
var uid=core.generateUid();
|
|
34
|
+
Resource.resourcesByUid.set(uid,new Resource(type,uid,url));
|
|
35
|
+
Resource.pathToUid.set(url,uid);
|
|
36
|
+
return uid;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function GetResourceFromUrl(url)
|
|
40
|
+
{
|
|
41
|
+
if(!Resource.pathToUid.has(url))
|
|
42
|
+
return null;
|
|
43
|
+
var uid=Resource.pathToUid.get(url);
|
|
44
|
+
if(uid==0)
|
|
45
|
+
return null;
|
|
46
|
+
var res=Resource.resourcesByUid.get(uid);
|
|
47
|
+
return res;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function GetResourceFromUid(uid)
|
|
51
|
+
{
|
|
52
|
+
var res=Resource.resourcesByUid.get(uid);
|
|
53
|
+
return res;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
//! Add the texture url as a resource.
|
|
57
|
+
function AddTexture(url)
|
|
58
|
+
{
|
|
59
|
+
GetResourceUidFromUrl(core.GeometryPayloadType.TexturePointer,url);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = {Resource,GetResourceFromUrl,GetResourceUidFromUrl,GetResourceFromUid,AddTexture};
|
package/scene/scene.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const nd = require('./node.js');
|
|
4
|
+
const core = require('../core/core.js');
|
|
5
|
+
const resources = require('./resources.js');
|
|
6
|
+
const { error } = require('console');
|
|
7
|
+
|
|
8
|
+
class Scene {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.nodes = new Map();
|
|
11
|
+
this.backgroundTexturePath="";
|
|
12
|
+
}
|
|
13
|
+
GetOrCreateNode(uid) {
|
|
14
|
+
if (!this.nodes.has(uid)) {
|
|
15
|
+
this.nodes.set(uid, new nd.Node(uid));
|
|
16
|
+
}
|
|
17
|
+
var c = this.nodes.get(uid);
|
|
18
|
+
return c;
|
|
19
|
+
}
|
|
20
|
+
GetNode(uid) {
|
|
21
|
+
if (!this.nodes.has(uid)) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
var c = this.nodes.get(uid);
|
|
25
|
+
return c;
|
|
26
|
+
}
|
|
27
|
+
CreateNode(name) {
|
|
28
|
+
var uid = core.generateUid();
|
|
29
|
+
if (this.nodes.has(uid)) {
|
|
30
|
+
error("Uid " + uid + " already present.");
|
|
31
|
+
}
|
|
32
|
+
this.nodes.set(uid, new nd.Node(uid, name));
|
|
33
|
+
return uid;
|
|
34
|
+
}
|
|
35
|
+
GetAllNodeUids() {
|
|
36
|
+
let node_uids = Array.from(this.nodes.keys());
|
|
37
|
+
return node_uids;
|
|
38
|
+
}
|
|
39
|
+
//! Load an initial scene state from a json file.
|
|
40
|
+
Load(filename) {
|
|
41
|
+
const data = fs.readFileSync(filename, "utf8");
|
|
42
|
+
const j = JSON.parse(data);
|
|
43
|
+
console.log(j);
|
|
44
|
+
if(j.environment)
|
|
45
|
+
{
|
|
46
|
+
if(j.environment.background_texture)
|
|
47
|
+
{
|
|
48
|
+
this.backgroundTexturePath=j.environment.background_texture;
|
|
49
|
+
resources.AddTexture(this.backgroundTexturePath);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if(j.nodes)
|
|
53
|
+
{
|
|
54
|
+
const j_nodes=j.nodes;
|
|
55
|
+
for (let [key, sub_obj] of Object.entries(j_nodes)) {
|
|
56
|
+
var uid = this.CreateNode();
|
|
57
|
+
var n = this.GetNode(uid);
|
|
58
|
+
n.name = key;
|
|
59
|
+
const pose = sub_obj["pose"];
|
|
60
|
+
if (pose) {
|
|
61
|
+
n.pose.position = { x: pose.position[0], y: pose.position[1], z: pose.position[2] };
|
|
62
|
+
n.pose.orientation = { x: pose.orientation[0], y: pose.orientation[1], z: pose.orientation[2], w: pose.orientation[3] };
|
|
63
|
+
}
|
|
64
|
+
const components = sub_obj["components"];
|
|
65
|
+
if (components) {
|
|
66
|
+
for (let c of components) {
|
|
67
|
+
if (c["type"] == "mesh")
|
|
68
|
+
n.setMeshComponent(c["url"]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
writeState() {
|
|
75
|
+
var content="";
|
|
76
|
+
this.nodes.forEach(node => {
|
|
77
|
+
content=content+"Node "+node.uid+" " + node.name + "<br>";
|
|
78
|
+
});
|
|
79
|
+
return content;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module.exports = { Scene };
|
package/server.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const WebRtcConnectionManager = require('./connections/webrtcconnectionmanager');
|
|
4
|
+
const cm = require('./client/client_manager.js');
|
|
5
|
+
|
|
6
|
+
const signaling=require("./signaling.js");
|
|
7
|
+
const scene=require("./scene/scene.js");
|
|
8
|
+
|
|
9
|
+
var sc=new scene.Scene();
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
const assetsPath = path.join(__dirname,'assets');
|
|
15
|
+
|
|
16
|
+
sc.Load(path.join(assetsPath,'scene.json'));
|
|
17
|
+
|
|
18
|
+
// This is our app's callback for when a new client is to be created.
|
|
19
|
+
// It must return the origin uid for the client.
|
|
20
|
+
function createNewClient(clientID) {
|
|
21
|
+
var origin_uid=sc.CreateNode();
|
|
22
|
+
return origin_uid;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// This will be called AFTER a client has been created, so we can access it from the clientManager.
|
|
26
|
+
function onClientPostCreate(clientID) {
|
|
27
|
+
var client=cm.getInstance().GetClient(clientID);
|
|
28
|
+
client.SetScene(sc);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const webRtcConnectionManager = WebRtcConnectionManager.getInstance();
|
|
32
|
+
webRtcConnectionManager.SetSendConfigMessage(signaling.sendConfigMessage);
|
|
33
|
+
cm.getInstance().SetNewClientCallback(createNewClient);
|
|
34
|
+
cm.getInstance().SetClientPostCreationCallback(onClientPostCreate);
|
|
35
|
+
signaling.init(webRtcConnectionManager,cm.getInstance().newClient.bind(cm.getInstance()));
|