janusweb 1.7.1 → 1.7.3
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/README.md +1 -1
- package/media/assets/webui/apps/comms/chat.html +6 -7
- package/media/assets/webui/apps/comms/comms.css +282 -84
- package/media/assets/webui/apps/comms/comms.html +6 -5
- package/media/assets/webui/apps/comms/comms.js +30 -14
- package/media/assets/webui/apps/comms/userlist.html +10 -17
- package/media/assets/webui/apps/comms/voip.js +111 -1
- package/media/assets/webui/apps/editor/editor-properties.js +34 -2
- package/media/assets/webui/apps/editor/editor.css +27 -0
- package/media/assets/webui/apps/editor/editor.js +326 -49
- package/media/assets/webui/apps/editor/objecticons-small.png +0 -0
- package/media/assets/webui/apps/inventory/inventory.css +71 -0
- package/media/assets/webui/apps/inventory/inventory.html +16 -61
- package/media/assets/webui/apps/inventory/inventory.js +342 -72
- package/media/assets/webui/apps/inventory/inventory.json +1 -4
- package/media/assets/webui/apps/inventory/objecticons-small.png +0 -0
- package/media/assets/webui/apps/xrfragment/level2-hyperlink.js +7 -5
- package/media/assets/webui/apps/xrfragment/xrfragment.json +1 -2
- package/package.json +2 -2
- package/scripts/client.js +4 -27
- package/scripts/config.js +7 -2
- package/scripts/janusbase.js +21 -3
- package/scripts/janusweb.js +24 -3
- package/scripts/object.js +67 -7
- package/scripts/room.js +113 -7
- package/media/assets/webui/apps/xrfragment/patch/metaquest-fix-flickering.js +0 -15
package/scripts/janusbase.js
CHANGED
|
@@ -278,6 +278,9 @@ elation.require(['engine.things.generic', 'utils.template', 'janusweb.parts'], f
|
|
|
278
278
|
this.refresh();
|
|
279
279
|
}
|
|
280
280
|
this.summarizeXML = function() {
|
|
281
|
+
// Without a tag there's no valid element to emit; returning markup here
|
|
282
|
+
// would produce a malformed nameless tag (e.g. "< />").
|
|
283
|
+
if (!this.tag) return '';
|
|
281
284
|
let proxy = this.getProxyObject(),
|
|
282
285
|
|
|
283
286
|
propdefs = this._thingdef.properties,
|
|
@@ -287,7 +290,9 @@ elation.require(['engine.things.generic', 'utils.template', 'janusweb.parts'], f
|
|
|
287
290
|
for (let k in proxydefs) {
|
|
288
291
|
let proxydef = proxydefs[k],
|
|
289
292
|
propdef = elation.utils.arrayget(propdefs, proxydef[1]);
|
|
290
|
-
|
|
293
|
+
// jsid / classname are read-aliases of js_id / class; emitting them too
|
|
294
|
+
// would duplicate the attribute on every element.
|
|
295
|
+
if ( k != 'room' && k != 'tagName' && k != 'classList' && k != 'jsid' && k != 'classname' && proxydef[0] == 'property' && propdef) {
|
|
291
296
|
let val = elation.utils.arrayget(this.properties, proxydef[1]);
|
|
292
297
|
let defaultval = propdef.default;
|
|
293
298
|
if (val instanceof THREE.Vector2) {
|
|
@@ -320,7 +325,16 @@ elation.require(['engine.things.generic', 'utils.template', 'janusweb.parts'], f
|
|
|
320
325
|
}
|
|
321
326
|
}
|
|
322
327
|
}
|
|
323
|
-
let
|
|
328
|
+
let tagname = this.tag.toLowerCase();
|
|
329
|
+
// A property whose name matches the tag is the element's content, not an
|
|
330
|
+
// attribute (<text>foo</text>, never <text text="foo" />). This mirrors the
|
|
331
|
+
// network serializer (getChanges) so source, save, and wire formats agree.
|
|
332
|
+
let content = null;
|
|
333
|
+
if (tagname in attrs) {
|
|
334
|
+
content = attrs[tagname];
|
|
335
|
+
delete attrs[tagname];
|
|
336
|
+
}
|
|
337
|
+
let xml = '<' + tagname;
|
|
324
338
|
for (let k in attrs) {
|
|
325
339
|
xml += ' ' + k + '="' + attrs[k] + '"';
|
|
326
340
|
}
|
|
@@ -330,13 +344,17 @@ elation.require(['engine.things.generic', 'utils.template', 'janusweb.parts'], f
|
|
|
330
344
|
children.push(k);
|
|
331
345
|
}
|
|
332
346
|
}
|
|
347
|
+
let hascontent = (content !== null && content !== '');
|
|
333
348
|
if (children.length > 0) {
|
|
334
349
|
xml += '>\n';
|
|
350
|
+
if (hascontent) xml += ' ' + content + '\n';
|
|
335
351
|
for (let i = 0; i < children.length; i++) {
|
|
336
352
|
let k = children[i];
|
|
337
353
|
xml += ' ' + this.children[k].summarizeXML().replace(/\n/g, '\n ').replace(/\s*$/, '\n');
|
|
338
354
|
}
|
|
339
|
-
xml += '</' +
|
|
355
|
+
xml += '</' + tagname + '>\n';
|
|
356
|
+
} else if (hascontent) {
|
|
357
|
+
xml += '>' + content + '</' + tagname + '>\n';
|
|
340
358
|
} else {
|
|
341
359
|
xml += ' />\n';
|
|
342
360
|
}
|
package/scripts/janusweb.js
CHANGED
|
@@ -61,7 +61,7 @@ elation.require([
|
|
|
61
61
|
this.defineProperties({
|
|
62
62
|
url: { type: 'string', default: false },
|
|
63
63
|
homepage: { type: 'string', default: "" },
|
|
64
|
-
corsproxy: { type: 'string', default:
|
|
64
|
+
corsproxy: { type: 'string', default: false },
|
|
65
65
|
showui: { type: 'boolean', default: true },
|
|
66
66
|
shownavigation: { type: 'boolean', default: true },
|
|
67
67
|
showchat: { type: 'boolean', default: true },
|
|
@@ -81,8 +81,19 @@ elation.require([
|
|
|
81
81
|
this.bookmarks = elation.collection.indexed({index: 'url', storagekey: 'janusweb.bookmarks'});
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
if (this.corsproxy != '') {
|
|
85
|
-
|
|
84
|
+
if (this.corsproxy !== false && this.corsproxy !== null && typeof this.corsproxy != 'undefined') {
|
|
85
|
+
// An explicit corsproxy attribute defines the full proxy set: a
|
|
86
|
+
// space-separated list becomes the rotation array, and the singular
|
|
87
|
+
// corsproxy is its first entry. The set is closed - the built-in
|
|
88
|
+
// proxy availability checks won't extend it. An empty or "false"
|
|
89
|
+
// value disables proxying entirely; omitting the attribute keeps
|
|
90
|
+
// the built-in defaults.
|
|
91
|
+
let corsproxies = (this.corsproxy === '' || this.corsproxy == 'false')
|
|
92
|
+
? []
|
|
93
|
+
: String(this.corsproxy).split(' ').filter(url => url);
|
|
94
|
+
corsproxies.explicit = true;
|
|
95
|
+
elation.config.set('engine.assets.corsproxies', corsproxies);
|
|
96
|
+
elation.engine.assets.setCORSProxy(corsproxies[0] || '');
|
|
86
97
|
}
|
|
87
98
|
this.assetpack = elation.engine.assets.loadAssetPack(this.properties.datapath + 'assets.json', this.properties.datapath);
|
|
88
99
|
this.parser = new JanusFireboxParser();
|
|
@@ -346,6 +357,16 @@ elation.require([
|
|
|
346
357
|
return this.load(dataurl, makeactive, baseurl)
|
|
347
358
|
}
|
|
348
359
|
this.setActiveRoom = function(url, referrer, skipURLUpdate) {
|
|
360
|
+
// skip sameroom urls (otherwise it restarts audio when
|
|
361
|
+
// activating portals to internal/sameroom objects like '#locationB' e.g.)
|
|
362
|
+
if( room && url.replace(/#.*/,'') == room.url ){
|
|
363
|
+
// XR Fragments spec (Level1: URL) https://xrfragment.org/#teleport%20camera
|
|
364
|
+
// update hash or assume 'spawn' as default spawn objectlocation
|
|
365
|
+
room.urlhash = url.match('#') ? url.replace(/.*#/,'') : 'spawn'
|
|
366
|
+
room.setPlayerPosition()
|
|
367
|
+
return
|
|
368
|
+
}
|
|
369
|
+
|
|
349
370
|
var oldroom = this.currentroom;
|
|
350
371
|
|
|
351
372
|
var room = false;
|
package/scripts/object.js
CHANGED
|
@@ -83,6 +83,9 @@ elation.require(['janusweb.janusbase', 'janusweb.websurface'], function() {
|
|
|
83
83
|
emissive_intensity: { type: 'float', default: 1, set: this.updateMaterial, comment: 'Intensity of material emissive color' },
|
|
84
84
|
roughness: { type: 'float', default: null, min: 0, max: 1, set: this.updateMaterial, comment: 'Material roughness value' },
|
|
85
85
|
metalness: { type: 'float', default: null, set: this.updateMaterial, comment: 'Material metalness value' },
|
|
86
|
+
sheen: { type: 'float', default: null, min: 0, max: 1, set: this.updateMaterial, comment: 'Sheen intensity (velvet/fabric); MeshPhysicalMaterial only' },
|
|
87
|
+
sheen_color: { type: 'color', default: null, set: this.updateMaterial, comment: 'Sheen tint colour' },
|
|
88
|
+
sheen_roughness: { type: 'float', default: null, min: 0, max: 1, set: this.updateMaterial, comment: 'Sheen roughness' },
|
|
86
89
|
transmission: { type: 'float', default: 0, set: this.updateMaterial, comment: 'Material transmission value' },
|
|
87
90
|
usevertexcolors: { type: 'boolean', default: true, set: this.updateMaterial },
|
|
88
91
|
gain: { type: 'float', default: 1.0, set: this.updateAudioNodes },
|
|
@@ -901,6 +904,17 @@ elation.require(['janusweb.janusbase', 'janusweb.websurface'], function() {
|
|
|
901
904
|
if (this.transmission !== null) {
|
|
902
905
|
m.transmission = this.transmission;
|
|
903
906
|
}
|
|
907
|
+
// Sheen (velvet/fabric). MeshPhysicalMaterial only; m.sheen is undefined on other
|
|
908
|
+
// material types, so these are no-ops there. Attributes default to null (unset).
|
|
909
|
+
if (this.sheen !== null && m.sheen !== undefined) {
|
|
910
|
+
m.sheen = this.sheen;
|
|
911
|
+
}
|
|
912
|
+
if (this.sheen_roughness !== null && m.sheenRoughness !== undefined) {
|
|
913
|
+
m.sheenRoughness = this.sheen_roughness;
|
|
914
|
+
}
|
|
915
|
+
if (this.sheen_color && m.sheenColor) {
|
|
916
|
+
m.sheenColor.copy(this.sheen_color);
|
|
917
|
+
}
|
|
904
918
|
|
|
905
919
|
if (this.isUsingPBR() && !this.isUsingToonShader()) {
|
|
906
920
|
m.envMap = this.getEnvmap();
|
|
@@ -969,7 +983,9 @@ elation.require(['janusweb.janusbase', 'janusweb.websurface'], function() {
|
|
|
969
983
|
} else {
|
|
970
984
|
m.vertexColors = false
|
|
971
985
|
}
|
|
972
|
-
m
|
|
986
|
+
if (!(m instanceof THREE.ShaderMaterial)) {
|
|
987
|
+
m.fog = this.fog;
|
|
988
|
+
}
|
|
973
989
|
m.wireframe = this.wireframe;
|
|
974
990
|
}
|
|
975
991
|
} else if (n instanceof THREE.Light && !this.lights) {
|
|
@@ -1103,19 +1119,63 @@ elation.require(['janusweb.janusbase', 'janusweb.websurface'], function() {
|
|
|
1103
1119
|
}
|
|
1104
1120
|
*/
|
|
1105
1121
|
|
|
1106
|
-
|
|
1107
|
-
|
|
1122
|
+
let chunkreplace = (this.shader_chunk_replace ? (elation.utils.isString(this.shader_chunk_replace) ? JSON.parse(this.shader_chunk_replace) : this.shader_chunk_replace) : null);
|
|
1123
|
+
// Parallax-corrected (box-projected) envmap reflections: opt-in per room via
|
|
1124
|
+
// envmap_parallax + envmap_box_center/size. Anchors reflections to a box (e.g.
|
|
1125
|
+
// the box bottom = floor) rather than the default infinite/camera-centered
|
|
1126
|
+
// projection, so a reflective floor mirrors the walls at the right world
|
|
1127
|
+
// position. Box uniforms are shared per-room objects, so live edits to the box
|
|
1128
|
+
// update every reflection without a recompile. Default off => stock behavior.
|
|
1129
|
+
let parallax = (this.isUsingPBR() && this.room && this.room.envmap_parallax) ? this.room.getEnvmapParallaxUniforms() : null;
|
|
1130
|
+
if (parallax) this.registerParallaxChunk();
|
|
1131
|
+
if (chunkreplace || parallax) {
|
|
1108
1132
|
m.onBeforeCompile = function(shader) {
|
|
1109
|
-
|
|
1110
|
-
let
|
|
1111
|
-
|
|
1112
|
-
|
|
1133
|
+
if (chunkreplace) {
|
|
1134
|
+
for (let oldchunkname in chunkreplace) {
|
|
1135
|
+
let newchunkname = chunkreplace[oldchunkname];
|
|
1136
|
+
shader.vertexShader = shader.vertexShader.replace('#include <' + oldchunkname + '>', '#include <' + newchunkname + '>');
|
|
1137
|
+
shader.fragmentShader = shader.fragmentShader.replace('#include <' + oldchunkname + '>', '#include <' + newchunkname + '>');
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
if (parallax) {
|
|
1141
|
+
shader.uniforms.boxProjMin = parallax.boxProjMin;
|
|
1142
|
+
shader.uniforms.boxProjMax = parallax.boxProjMax;
|
|
1143
|
+
shader.uniforms.boxProjCenter = parallax.boxProjCenter;
|
|
1144
|
+
shader.vertexShader = 'varying vec3 vParallaxWorldPos;\n' + shader.vertexShader.replace(
|
|
1145
|
+
'#include <project_vertex>',
|
|
1146
|
+
'#include <project_vertex>\n\tvParallaxWorldPos = ( modelMatrix * vec4( transformed, 1.0 ) ).xyz;'
|
|
1147
|
+
);
|
|
1148
|
+
shader.fragmentShader = shader.fragmentShader.replace(
|
|
1149
|
+
'#include <envmap_physical_pars_fragment>',
|
|
1150
|
+
'#include <envmap_physical_pars_fragment_parallax>'
|
|
1151
|
+
);
|
|
1113
1152
|
}
|
|
1114
1153
|
};
|
|
1115
1154
|
}
|
|
1116
1155
|
}
|
|
1117
1156
|
return m;
|
|
1118
1157
|
}
|
|
1158
|
+
this.registerParallaxChunk = function() {
|
|
1159
|
+
// A box-projected variant of three's envmap_physical_pars_fragment: after the
|
|
1160
|
+
// reflection vector is put into world space it's intersected with the box
|
|
1161
|
+
// (boxProjMin..boxProjMax) and re-based on boxProjCenter, so the CubeUV/PMREM
|
|
1162
|
+
// sample is parallax-corrected. Built by patching the stock chunk so it tracks
|
|
1163
|
+
// the exact r150 source; if the source ever changes and the anchor line isn't
|
|
1164
|
+
// found, this degrades to the stock (infinite) chunk rather than breaking.
|
|
1165
|
+
if (THREE.ShaderChunk['envmap_physical_pars_fragment_parallax']) return;
|
|
1166
|
+
let anchor = 'reflectVec = inverseTransformDirection( reflectVec, viewMatrix );';
|
|
1167
|
+
let patched = THREE.ShaderChunk['envmap_physical_pars_fragment']
|
|
1168
|
+
.replace('#if defined( USE_ENVMAP )',
|
|
1169
|
+
'#if defined( USE_ENVMAP )\n\tuniform vec3 boxProjMin;\n\tuniform vec3 boxProjMax;\n\tuniform vec3 boxProjCenter;\n\tvarying vec3 vParallaxWorldPos;')
|
|
1170
|
+
.replace(anchor, anchor +
|
|
1171
|
+
'\n\t\t\t{ vec3 nrd = normalize( reflectVec );' +
|
|
1172
|
+
'\n\t\t\t vec3 rbmax = ( boxProjMax - vParallaxWorldPos ) / nrd;' +
|
|
1173
|
+
'\n\t\t\t vec3 rbmin = ( boxProjMin - vParallaxWorldPos ) / nrd;' +
|
|
1174
|
+
'\n\t\t\t vec3 rbmm = vec3( ( nrd.x > 0.0 ) ? rbmax.x : rbmin.x, ( nrd.y > 0.0 ) ? rbmax.y : rbmin.y, ( nrd.z > 0.0 ) ? rbmax.z : rbmin.z );' +
|
|
1175
|
+
'\n\t\t\t float fa = min( min( rbmm.x, rbmm.y ), rbmm.z );' +
|
|
1176
|
+
'\n\t\t\t reflectVec = ( vParallaxWorldPos + nrd * fa ) - boxProjCenter; }');
|
|
1177
|
+
THREE.ShaderChunk['envmap_physical_pars_fragment_parallax'] = patched;
|
|
1178
|
+
}
|
|
1119
1179
|
this.updateSkybox = function() {
|
|
1120
1180
|
let envMap = (this.isUsingPBR() ? this.getEnvmap() : null);
|
|
1121
1181
|
this.traverseObjects(n => {
|
package/scripts/room.js
CHANGED
|
@@ -55,6 +55,13 @@ elation.require([
|
|
|
55
55
|
'skybox_back_id': { type: 'string', set: this.setSkybox },
|
|
56
56
|
'cubemap_irradiance_id': { type: 'string' },
|
|
57
57
|
'cubemap_radiance_id': { type: 'string' },
|
|
58
|
+
// Envmap reflection projection. Default off = infinite (camera-centered)
|
|
59
|
+
// projection. When on, reflections are box-projected (parallax-corrected) to
|
|
60
|
+
// the box defined by envmap_box_center/size -- put the box bottom at the floor
|
|
61
|
+
// for clean floor reflections of the walls.
|
|
62
|
+
'envmap_parallax': { type: 'boolean', default: false, set: this.updateParallax },
|
|
63
|
+
'envmap_box_center': { type: 'vector3', default: new THREE.Vector3(0, 0, 0), set: this.updateParallax },
|
|
64
|
+
'envmap_box_size': { type: 'vector3', default: new THREE.Vector3(10, 10, 10), set: this.updateParallax },
|
|
58
65
|
'fog': { type: 'boolean', default: false, set: this.setFog },
|
|
59
66
|
'fog_mode': { type: 'string', default: 'exp', set: this.setFog },
|
|
60
67
|
'fog_density': { type: 'float', default: 1.0, set: this.setFog },
|
|
@@ -326,6 +333,35 @@ elation.require([
|
|
|
326
333
|
}
|
|
327
334
|
return spawnpoint;
|
|
328
335
|
}
|
|
336
|
+
// Shared box-projection uniforms for parallax-corrected envmap reflections. One
|
|
337
|
+
// set of THREE uniform objects per room; every PBR material's parallax shader
|
|
338
|
+
// references these same objects, so live edits to envmap_box_center/size update
|
|
339
|
+
// all reflections in place (no material recompile). See object.registerParallaxChunk.
|
|
340
|
+
this.getEnvmapParallaxUniforms = function() {
|
|
341
|
+
if (!this._parallaxUniforms) {
|
|
342
|
+
this._parallaxUniforms = {
|
|
343
|
+
boxProjMin: { value: new THREE.Vector3() },
|
|
344
|
+
boxProjMax: { value: new THREE.Vector3() },
|
|
345
|
+
boxProjCenter: { value: new THREE.Vector3() },
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
this.updateParallaxUniforms();
|
|
349
|
+
return this._parallaxUniforms;
|
|
350
|
+
}
|
|
351
|
+
this.updateParallaxUniforms = function() {
|
|
352
|
+
if (!this._parallaxUniforms) return;
|
|
353
|
+
let c = this.envmap_box_center || new THREE.Vector3();
|
|
354
|
+
let s = this.envmap_box_size || new THREE.Vector3(10, 10, 10);
|
|
355
|
+
this._parallaxUniforms.boxProjCenter.value.copy(c);
|
|
356
|
+
this._parallaxUniforms.boxProjMin.value.set(c.x - s.x / 2, c.y - s.y / 2, c.z - s.z / 2);
|
|
357
|
+
this._parallaxUniforms.boxProjMax.value.set(c.x + s.x / 2, c.y + s.y / 2, c.z + s.z / 2);
|
|
358
|
+
}
|
|
359
|
+
this.updateParallax = function() {
|
|
360
|
+
// Live box edits mutate the shared uniforms in place -> all reflections follow
|
|
361
|
+
// without a recompile. (Enabling/disabling envmap_parallax itself is applied at
|
|
362
|
+
// material-build time / room load.)
|
|
363
|
+
this.updateParallaxUniforms();
|
|
364
|
+
}
|
|
329
365
|
this.setSkybox = function() {
|
|
330
366
|
if (!this.loaded) return;
|
|
331
367
|
|
|
@@ -818,24 +854,48 @@ elation.require([
|
|
|
818
854
|
room[k] = val;
|
|
819
855
|
}
|
|
820
856
|
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
857
|
+
// Apply edits to every element type, not just <Object>. The parser groups
|
|
858
|
+
// <text>, <image>, <link>, etc. under their own keys, so iterating only
|
|
859
|
+
// `roomdata.object` silently dropped source edits to all other elements.
|
|
860
|
+
var skiptypes = ['assets', 'room', 'source'];
|
|
861
|
+
for (let type in roomdata) {
|
|
862
|
+
if (skiptypes.indexOf(type) != -1) continue;
|
|
863
|
+
let objs = roomdata[type];
|
|
864
|
+
if (!elation.utils.isArray(objs)) objs = [objs];
|
|
865
|
+
for (let i = 0; i < objs.length; i++) {
|
|
866
|
+
let objdata = objs[i];
|
|
824
867
|
let roomobj = room.objects[objdata.js_id];
|
|
825
868
|
if (roomobj) {
|
|
826
869
|
for (let k in objdata) {
|
|
870
|
+
// `orientation` is derived by the parser from xdir/ydir/zdir and
|
|
871
|
+
// defaults to identity when only `rotation` is authored, so it
|
|
872
|
+
// disagrees with `rotation`. Applying both fights; the authored
|
|
873
|
+
// rotation / direction attributes are what drive orientation.
|
|
874
|
+
if (k === 'orientation' || k === '_content') continue;
|
|
827
875
|
let val = objdata[k];
|
|
828
|
-
if (roomobj[k] !=
|
|
876
|
+
if (roomobj[k] != val && val !== null) {
|
|
877
|
+
// Flag dirty so source edits broadcast to other clients; set
|
|
878
|
+
// per change because onThingChange clears sync once tracked.
|
|
879
|
+
roomobj.sync = true;
|
|
829
880
|
roomobj[k] = val;
|
|
830
881
|
}
|
|
831
882
|
}
|
|
883
|
+
// Elements that carry their value as tag content (<text>foo</text>)
|
|
884
|
+
// parse to `_content`; the receiving property is named after the tag.
|
|
885
|
+
if (objdata._content != null && roomobj[type] != objdata._content) {
|
|
886
|
+
roomobj.sync = true;
|
|
887
|
+
roomobj[type] = objdata._content;
|
|
888
|
+
}
|
|
832
889
|
} else {
|
|
833
890
|
objdata.persist = true;
|
|
834
|
-
|
|
891
|
+
if (objdata._content != null && objdata[type] == null) objdata[type] = objdata._content;
|
|
892
|
+
this.createObject(type, objdata);
|
|
835
893
|
}
|
|
836
894
|
}
|
|
837
895
|
}
|
|
838
896
|
}
|
|
897
|
+
// Editor surfaces (scene tree, inspector, source view) refresh from this.
|
|
898
|
+
this.notifySceneChanged();
|
|
839
899
|
}
|
|
840
900
|
|
|
841
901
|
this.loadRoomAssets = function(roomdata) {
|
|
@@ -988,7 +1048,11 @@ elation.require([
|
|
|
988
1048
|
|
|
989
1049
|
if (room.cubemap_radiance_id) this.properties.cubemap_radiance_id = room.cubemap_radiance_id;
|
|
990
1050
|
if (room.cubemap_irradiance_id) this.properties.cubemap_irradiance_id = room.cubemap_irradiance_id;
|
|
991
|
-
|
|
1051
|
+
|
|
1052
|
+
if (typeof room.envmap_parallax != 'undefined') this.envmap_parallax = room.envmap_parallax;
|
|
1053
|
+
if (room.envmap_box_center) this.envmap_box_center = room.envmap_box_center;
|
|
1054
|
+
if (room.envmap_box_size) this.envmap_box_size = room.envmap_box_size;
|
|
1055
|
+
|
|
992
1056
|
this.setSkybox();
|
|
993
1057
|
|
|
994
1058
|
if (room.server) this.properties.server = room.server;
|
|
@@ -1282,6 +1346,9 @@ elation.require([
|
|
|
1282
1346
|
this.applyingEdits = false;
|
|
1283
1347
|
this.appliedchanges = {};
|
|
1284
1348
|
|
|
1349
|
+
// Editor surfaces reflect remote edits (onThingChange is gated out while
|
|
1350
|
+
// applyingEdits, so signal here instead).
|
|
1351
|
+
this.notifySceneChanged();
|
|
1285
1352
|
}
|
|
1286
1353
|
this.applyDeleteXML = function(deletexml) {
|
|
1287
1354
|
var del = elation.utils.parseXML(deletexml);
|
|
@@ -1913,6 +1980,18 @@ elation.require([
|
|
|
1913
1980
|
soundsystem.enableSound();
|
|
1914
1981
|
}
|
|
1915
1982
|
}
|
|
1983
|
+
// Room-level "scene changed" signal that editor surfaces subscribe to
|
|
1984
|
+
// (source-view auto-refresh, scene-tree/inspector reflect). Trailing-
|
|
1985
|
+
// debounced so a gizmo drag or burst of edits coalesces into one fire on
|
|
1986
|
+
// settle. Only the edit paths call it, so animation/physics ticks never
|
|
1987
|
+
// reach here; structural changes are signalled via thing_add/thing_remove.
|
|
1988
|
+
this.notifySceneChanged = function() {
|
|
1989
|
+
clearTimeout(this._sceneChangedTimer);
|
|
1990
|
+
this._sceneChangedTimer = setTimeout(elation.bind(this, function() {
|
|
1991
|
+
// Fire on the proxy (window.room) since editor surfaces listen there.
|
|
1992
|
+
elation.events.fire({type: 'scene_changed', element: this.getProxyObject()});
|
|
1993
|
+
}), 150);
|
|
1994
|
+
}
|
|
1916
1995
|
this.onThingChange = function(ev) {
|
|
1917
1996
|
if (this.applyingEdits) return;
|
|
1918
1997
|
|
|
@@ -1928,6 +2007,7 @@ elation.require([
|
|
|
1928
2007
|
this.changes[js_id] = proxy;
|
|
1929
2008
|
}
|
|
1930
2009
|
thing.properties.sync = proxy.autosync;
|
|
2010
|
+
this.notifySceneChanged(); // a real edit (sync set) -> refresh editor surfaces
|
|
1931
2011
|
}
|
|
1932
2012
|
}
|
|
1933
2013
|
}
|
|
@@ -1945,6 +2025,7 @@ elation.require([
|
|
|
1945
2025
|
if (this.jsobjects[thing.js_id]) {
|
|
1946
2026
|
delete this.jsobjects[thing.js_id];
|
|
1947
2027
|
}
|
|
2028
|
+
this.notifySceneChanged();
|
|
1948
2029
|
}
|
|
1949
2030
|
this.onRoomEdit = function(ev) {
|
|
1950
2031
|
var thing = ev.data;
|
|
@@ -2184,6 +2265,9 @@ elation.require([
|
|
|
2184
2265
|
skybox_down_id: ['property', 'skybox_down_id'],
|
|
2185
2266
|
skybox_front_id:['property', 'skybox_front_id'],
|
|
2186
2267
|
skybox_back_id: ['property', 'skybox_back_id'],
|
|
2268
|
+
envmap_parallax: ['property', 'envmap_parallax'],
|
|
2269
|
+
envmap_box_center: ['property', 'envmap_box_center'],
|
|
2270
|
+
envmap_box_size: ['property', 'envmap_box_size'],
|
|
2187
2271
|
|
|
2188
2272
|
pendingScripts: ['property', 'pendingScripts'],
|
|
2189
2273
|
pendingCustomElements: ['property', 'pendingCustomElements'],
|
|
@@ -2590,7 +2674,11 @@ elation.require([
|
|
|
2590
2674
|
//for (var k in this.jsobjects) {
|
|
2591
2675
|
for (let k in this.children) {
|
|
2592
2676
|
var object = this.children[k];
|
|
2593
|
-
|
|
2677
|
+
// Only persistent room objects belong in the source. Transient children
|
|
2678
|
+
// (the local player, remote-player ghosts, cursors) are not part of the
|
|
2679
|
+
// room markup and have no JanusML tag, so serializing them would emit
|
|
2680
|
+
// malformed nameless tags.
|
|
2681
|
+
if (object.persist && object.janus && typeof object.summarizeXML == 'function') {
|
|
2594
2682
|
var markup = ' ' + object.summarizeXML().replace(/\n/g, '\n ').replace(/\s*$/, '\n');
|
|
2595
2683
|
objectsrc += markup;
|
|
2596
2684
|
}
|
|
@@ -2603,6 +2691,24 @@ elation.require([
|
|
|
2603
2691
|
|
|
2604
2692
|
return roomsrc;
|
|
2605
2693
|
}
|
|
2694
|
+
// Per-object serialization of the persistent top-level objects, for editors
|
|
2695
|
+
// that reconcile an authored source against the live scene without
|
|
2696
|
+
// regenerating the whole document.
|
|
2697
|
+
this.getObjectSummaries = function() {
|
|
2698
|
+
var out = [];
|
|
2699
|
+
for (let k in this.children) {
|
|
2700
|
+
var object = this.children[k];
|
|
2701
|
+
if (object.persist && object.janus && typeof object.summarizeXML == 'function' && object.tag) {
|
|
2702
|
+
out.push({
|
|
2703
|
+
js_id: object.js_id,
|
|
2704
|
+
id: object.id,
|
|
2705
|
+
name: String(object.tag).toLowerCase(),
|
|
2706
|
+
xml: object.summarizeXML().replace(/\s+$/, '')
|
|
2707
|
+
});
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
return out;
|
|
2711
|
+
}
|
|
2606
2712
|
|
|
2607
2713
|
// FIXME - room should inherit from janusbase and get this automatically
|
|
2608
2714
|
this.addEventListenerProxy = function(name, handler, bubble) {
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
// Meta Quest 2 results in rapid flickering when loading a new room
|
|
2
|
-
// therefore we show the shroud while loading
|
|
3
|
-
setActiveRoom = room.janus.setActiveRoom.bind(room.janus)
|
|
4
|
-
room.janus.setActiveRoom = function(url,referer,skipURL){
|
|
5
|
-
if( url.replace(/#.*/,'') != room.url ){
|
|
6
|
-
room.fadeAudioOut()
|
|
7
|
-
setTimeout( function(){
|
|
8
|
-
setActiveRoom.apply(room.janus, [url, referer, skipURL])
|
|
9
|
-
}, 500)
|
|
10
|
-
}else{
|
|
11
|
-
room.urlhash = url.match('#') ? url.replace(/.*#/,'') : 'spawn'
|
|
12
|
-
room.setPlayerPosition()
|
|
13
|
-
// ignore same-room setActiveRoom-calls (it restarts audio when clicking internal hyperlinks)
|
|
14
|
-
}
|
|
15
|
-
}
|