janusweb 1.5.30 → 1.5.34

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.
Files changed (84) hide show
  1. package/CHANGELOG +39 -0
  2. package/LICENSE +1 -1
  3. package/media/assets/janus-avatar-animations.glb +0 -0
  4. package/media/assets/janus-avatar-base.glb +0 -0
  5. package/media/assets/webui/apps/avatar/avatar.css +6 -2
  6. package/media/assets/webui/apps/avatar/avatar.js +99 -11
  7. package/media/assets/webui/apps/buttons/buttons.html +2 -2
  8. package/media/assets/webui/apps/buttons/buttons.js +3 -3
  9. package/media/assets/webui/apps/comms/comms.js +7 -4
  10. package/media/assets/webui/apps/comms/voip.js +133 -57
  11. package/media/assets/webui/apps/editor/codemirror/addon/hint/javascript-hint.js +162 -0
  12. package/media/assets/webui/apps/editor/codemirror/addon/hint/show-hint.css +36 -0
  13. package/media/assets/webui/apps/editor/codemirror/addon/hint/show-hint.js +529 -0
  14. package/media/assets/webui/apps/editor/codemirror/addon/hint/xml-hint.js +140 -0
  15. package/media/assets/webui/apps/editor/codemirror/keymap/vim.js +5734 -0
  16. package/media/assets/webui/apps/editor/codemirror/lib/codemirror.css +350 -0
  17. package/media/assets/webui/apps/editor/codemirror/lib/codemirror.js +9800 -0
  18. package/media/assets/webui/apps/editor/codemirror/lib/jmldark.css +33 -0
  19. package/media/assets/webui/apps/editor/codemirror/mode/css/css.js +864 -0
  20. package/media/assets/webui/apps/editor/codemirror/mode/htmlmixed/htmlmixed.js +153 -0
  21. package/media/assets/webui/apps/editor/codemirror/mode/javascript/javascript.js +942 -0
  22. package/media/assets/webui/apps/editor/codemirror/mode/xml/xml.js +413 -0
  23. package/media/assets/webui/apps/editor/editor.css +3 -0
  24. package/media/assets/webui/apps/editor/editor.js +409 -9
  25. package/media/assets/webui/apps/editor/editor.json +6 -3
  26. package/media/assets/webui/apps/inventory/inventory.html +7 -4
  27. package/media/assets/webui/apps/inventory/inventory.js +2 -2
  28. package/media/assets/webui/apps/locomotion/teleporter.js +10 -9
  29. package/media/assets/webui/apps/navigation/navigation.js +12 -12
  30. package/media/assets/webui/apps/settings/settings.html +1 -1
  31. package/media/assets/webui/apps/vfs/browserfs/browserfs.js +8 -0
  32. package/media/assets/webui/apps/vfs/vfs.css +52 -0
  33. package/media/assets/webui/apps/vfs/vfs.js +452 -0
  34. package/media/assets/webui/apps/vfs/vfs.json +10 -0
  35. package/media/assets/webui/apps/virtualgamepad/virtualgamepad.js +9 -7
  36. package/media/assets/webui/apps/xrmenu/xrmenu.js +13 -4
  37. package/media/assets/webui/default.json +2 -1
  38. package/media/assets/webui/none.json +1 -0
  39. package/media/assets/webui/themes/default.css +29 -2
  40. package/media/assets.json +2 -1
  41. package/media/index.html +1 -1
  42. package/media/lib/draco/README.md +32 -0
  43. package/media/lib/draco/draco_decoder.js +52 -0
  44. package/media/lib/draco/draco_decoder.wasm +0 -0
  45. package/media/lib/draco/draco_encoder.js +33 -0
  46. package/media/lib/draco/draco_wasm_wrapper.js +104 -0
  47. package/media/lib/draco/gltf/draco_decoder.js +48 -0
  48. package/media/lib/draco/gltf/draco_decoder.wasm +0 -0
  49. package/media/lib/draco/gltf/draco_encoder.js +33 -0
  50. package/media/lib/draco/gltf/draco_wasm_wrapper.js +104 -0
  51. package/media/lib/physx/physx.release.js +21 -0
  52. package/media/lib/physx/physx.release.wasm +0 -0
  53. package/media/manifest.json +5 -5
  54. package/media/service-worker.js +7 -0
  55. package/package.json +2 -9
  56. package/scripts/client.js +67 -46
  57. package/scripts/config.js +2 -0
  58. package/scripts/elements/linesegments.js +10 -3
  59. package/scripts/external/JanusFireboxParser.js +1 -0
  60. package/scripts/image.js +7 -1
  61. package/scripts/janusbase.js +104 -43
  62. package/scripts/janusghost.js +314 -16
  63. package/scripts/januslight.js +11 -2
  64. package/scripts/janusparagraph.js +8 -6
  65. package/scripts/janusparticle.js +26 -16
  66. package/scripts/janusplayer.js +126 -37
  67. package/scripts/janusweb.js +26 -2
  68. package/scripts/janusxrplayer.js +5 -3
  69. package/scripts/multiplayermanager.js +6 -4
  70. package/scripts/object.js +246 -88
  71. package/scripts/portal.js +23 -14
  72. package/scripts/remoteplayer.js +1 -25
  73. package/scripts/room.js +199 -77
  74. package/scripts/sound.js +8 -6
  75. package/scripts/text.js +13 -5
  76. package/scripts/translators/janusvfs.js +20 -0
  77. package/scripts/video.js +1 -1
  78. package/scripts/websurface.js +17 -3
  79. package/utils/build.sh +3 -0
  80. package/utils/init.sh +2 -2
  81. package/scripts/chat.js +0 -80
  82. package/scripts/ui.js +0 -265
  83. package/scripts/urlbar.js +0 -93
  84. package/templates/janusweb.tpl +0 -3
@@ -1,4 +1,5 @@
1
1
  elation.require(['janusweb.janusbase', 'engine.things.leapmotion'], function() {
2
+
2
3
  elation.component.add('engine.things.janusghost', function() {
3
4
  this.postinit = function() {
4
5
  elation.engine.things.janusghost.extendclass.postinit.call(this);
@@ -16,9 +17,29 @@ elation.require(['janusweb.janusbase', 'engine.things.leapmotion'], function() {
16
17
  ghostassets: { type: 'object' },
17
18
  auto_play: { type: 'boolean', default: true },
18
19
  screen_name: { type: 'string' },
20
+ animation_extras: { type: 'string' },
21
+ bone_head: { type: 'string' },
22
+ morphtarget_mouth: { type: 'string' },
23
+ morphtarget_eyes: { type: 'string' },
19
24
  });
20
25
 
21
26
  this.frames = false;
27
+
28
+ if (!('project_vertex_discard_close' in THREE.ShaderChunk)) {
29
+ THREE.ShaderChunk['color_fragment_discard_close'] = `
30
+ #if defined( USE_COLOR_ALPHA )
31
+ diffuseColor *= vColor;
32
+ #elif defined( USE_COLOR )
33
+ diffuseColor.rgb *= vColor;
34
+ #endif
35
+ float dist = length(vViewPosition);
36
+ float mindist = .4;
37
+ float maxdist = .6;
38
+ if (dist < maxdist) {
39
+ diffuseColor.a = clamp(((dist - mindist) / (maxdist - mindist)), 0., 1.);
40
+ }
41
+ `;
42
+ }
22
43
  }
23
44
  this.createObject3D = function() {
24
45
  if (this.ghost_src) {
@@ -76,7 +97,6 @@ elation.require(['janusweb.janusbase', 'engine.things.leapmotion'], function() {
76
97
  size: .1,
77
98
  thickness: .03,
78
99
  align: 'center',
79
- collidable: false,
80
100
  text: name,
81
101
  pos: this.userid_pos,
82
102
  pickable: false,
@@ -114,6 +134,45 @@ elation.require(['janusweb.janusbase', 'engine.things.leapmotion'], function() {
114
134
  console.log(this.ghostassets);
115
135
  this.assetpack.loadJSON(assets.assetlist);
116
136
  }
137
+
138
+ let animnames = []; //'idle', 'walk', 'walk_left', 'walk_right', 'walk_back', 'run', 'jump', 'fly', 'speak', 'type', 'portal'];
139
+ let animassets = assets.assetlist.filter(asset => animnames.indexOf(asset.name) != -1);
140
+ console.log('some ghost animations', animassets, assets);
141
+
142
+ if (!this.animationmixer) {
143
+ // Set up our animation mixer with a simple bone mapper for our head. We'll add more animations to this ass other assets load
144
+ // TODO - this is probably also where we'd map any other tracked objects (hands, hips, etc). and set up IK
145
+
146
+ /*
147
+ let headtrack = new THREE.QuaternionKeyframeTrack('Neck.quaternion', [0], [0, 0, 0, 1]),
148
+ headclip = new THREE.AnimationClip('head_rotation', -1, [headtrack]);
149
+ this.headtrack = headtrack;
150
+
151
+ this.initAnimations([ headclip ]);
152
+ //this.animations['head_rotation'].play();
153
+ */
154
+ this.initAnimations([]);
155
+ }
156
+
157
+ animassets.forEach(anim => {
158
+ let asset = this.getAsset('model', anim.name);
159
+ console.log('try to load the animation', asset, anim);
160
+ if (asset) {
161
+ if (!asset.loaded) {
162
+ let model = asset.getInstance();
163
+ elation.events.add(asset, 'asset_load', ev => {
164
+ let clip = false;
165
+ model.traverse(n => { if (n.animations && n.animations.length > 0) clip = n.animations[0]; });
166
+ if (clip) {
167
+ if (this.animationmixer && !this.animations[anim.name]) {
168
+ let action = this.animationmixer.clipAction(clip);
169
+ this.animations[anim.name] = action;
170
+ }
171
+ }
172
+ });
173
+ }
174
+ }
175
+ });
117
176
  }
118
177
  this.getGhostObjects = function() {
119
178
  var objects = {};
@@ -186,6 +245,8 @@ elation.require(['janusweb.janusbase', 'engine.things.leapmotion'], function() {
186
245
  this.head.appendChild(headid);
187
246
  this.face.start();
188
247
  }
248
+ this.head.pos = headpos.clone().multiplyScalar(this.scale.y);
249
+ this.face.applyPosition(headpos.negate());
189
250
  if (this.remotevideo) {
190
251
  this.updateVideoScreen();
191
252
  this.face.addEventListener('load', (ev) => {
@@ -217,6 +278,11 @@ elation.require(['janusweb.janusbase', 'engine.things.leapmotion'], function() {
217
278
  //rotation: V(0, 180, 0),
218
279
  lighting: this.lighting,
219
280
  //cull_face: 'none'
281
+ opacity: 0.9999,
282
+ renderorder: this.renderorder || 100,
283
+ shader_chunk_replace: (this.lighting ? {
284
+ 'color_fragment': 'color_fragment_discard_close',
285
+ } : {}),
220
286
  });
221
287
  } else {
222
288
  this.body = bodyid;
@@ -225,11 +291,169 @@ elation.require(['janusweb.janusbase', 'engine.things.leapmotion'], function() {
225
291
  if (pos) this.body.pos = pos;
226
292
  this.body.start();
227
293
  if (scale && this.body) this.body.scale.fromArray(scale);
294
+
295
+ elation.events.add(this, 'load', ev => {
296
+ if (this.body.modelasset) {
297
+ if (this.body.modelasset.loaded) {
298
+ this.loadAnimations();
299
+ if (this.animation_extras) {
300
+ this.loadAnimationExtras();
301
+ }
302
+ } else {
303
+ elation.events.add(this.body.modelasset, 'asset_load_complete', () => {
304
+ this.loadAnimations();
305
+ if (this.animation_extras) {
306
+ this.loadAnimationExtras();
307
+ }
308
+ });
309
+ }
310
+ }
311
+ // FIXME - this prevents avatars from being culled, which prevents our default mesh from disappearing but also means we lose out on possible performance optimizations in rooms with lots of avatars
312
+ this.objects['3d'].traverse(n => {
313
+ if (n instanceof THREE.Mesh) {
314
+ n.frustumCulled = false;
315
+ }
316
+ });
317
+ });
318
+ }
319
+ }
320
+ this.loadAnimations = function(assetid) {
321
+ if (!assetid) assetid = 'avatar_animations';
322
+ let animasset = this.getAsset('model', assetid);
323
+ if (!animasset.loaded) {
324
+ let animationsLoaded = false;
325
+ elation.events.add(animasset, 'asset_load_complete', ev => {
326
+ if (!animationsLoaded) {
327
+ animationsLoaded = true;
328
+ if (this.body.animationmixer) {
329
+ this.cloneAnimations(animasset);
330
+ }
331
+ this.body.setAnimation('idle');
332
+ }
333
+ });
334
+ animasset.load();
335
+ } else {
336
+ if (this.body.animationmixer) {
337
+ this.cloneAnimations(animasset);
338
+ }
339
+ this.body.setAnimation('idle');
340
+ }
341
+ }
342
+ this.cloneAnimations = function(animasset) {
343
+ let animations = animasset.animations;
344
+ //console.log('clone all the animations', animations, animasset._model);
345
+ if (this.body) {
346
+ if (this.bone_head) {
347
+ this.initHeadAnimation(animasset);
348
+ }
349
+
350
+ animations.forEach(clip => {
351
+ this.body.animations[clip.name] = this.body.animationmixer.clipAction(this.retargetAnimation(clip, animasset._model));
352
+ //console.log('new clip', clip.name, clip, this.body.animations[clip.name]);
353
+ });
354
+ //console.log('head rot', this.body.animations['head_rotation'], headclip, headaction);
355
+ //console.log(this.body.modelasset, this.body.modelasset.vrm);
356
+ if (this.body.modelasset && this.body.modelasset.vrm) {
357
+ let rename = {};
358
+ let bonemap = this.body.modelasset.vrm.humanoid.humanBones;
359
+ for (let k in bonemap) {
360
+ console.log(k, bonemap[k]);
361
+ if (bonemap[k].length > 0) {
362
+ rename[bonemap[k][0].node.name] = k;
363
+ }
364
+ }
365
+ let bones = [];
366
+ let meshes = [];
367
+ this.body.objects['3d'].traverse(n => { if (n instanceof THREE.Bone) bones.push(n); else if (n instanceof THREE.SkinnedMesh) meshes.push(n); });
368
+ /*
369
+ console.log('rename the bones!', rename);
370
+ THREE.SkeletonUtils.renameBones(bones, rename);
371
+ console.log('bones now', bones);
372
+ */
373
+
374
+ }
228
375
  }
229
376
  }
377
+ this.retargetAnimation = function(clip, sourcecontainer) {
378
+ let newclip = clip.clone();
379
+ let sourcemesh = null,
380
+ destmesh = null;
381
+ if (sourcecontainer) {
382
+ sourcecontainer.traverse(n => { /*console.log(' - ', n); */ if (n instanceof THREE.SkinnedMesh && n.skeleton) sourcemesh = n; });
383
+ }
384
+ this.objects['3d'].traverse(n => { if (n instanceof THREE.SkinnedMesh && n.skeleton) destmesh = n; });
385
+ //console.log('RETARGET ANI*MATION', clip, sourcemesh, destmesh, sourcecontainer);
386
+ let remove = [];
387
+ if (this.body && this.body.modelasset && this.body.modelasset.vrm) {
388
+ let vrm = this.body.modelasset.vrm;
389
+ //console.log('RETARGET ANI*MATION', clip, this.body.modelasset.vrm, sourcemesh, destmesh);
390
+ newclip.tracks.forEach(track => {
391
+ let parts = track.name.split('.');
392
+ console.log(parts, track.name);
393
+ try {
394
+ let bone = vrm.humanoid.getBone(parts[0]);
395
+ if (bone) {
396
+ track.name = bone.node.name + '.' + parts[1];
397
+ let sourcebone = sourcemesh.skeleton.getBone(bone.node.name);
398
+ //console.log(track, bone);
399
+ } else {
400
+ //console.log('no bone!', parts, track);
401
+ }
402
+ } catch (e) {
403
+ console.log('omgwtf', e.message);
404
+ remove.push(track);
405
+ }
406
+ });
407
+ remove.forEach(track => {
408
+ let idx = newclip.tracks.indexOf(track);
409
+ if (idx != -1) {
410
+ newclip.tracks.splice(idx, 1);
411
+ //console.log('removed track', track);
412
+ }
413
+ });
414
+ } else if (destmesh && sourcemesh) {
415
+ // FIXME - silly hack to store skeleton, we should just be extracting it at load time
416
+ this.body.skeleton = destmesh.skeleton;
417
+ this.body.objects['3d'].skeleton = destmesh.skeleton;
418
+ //newclip = THREE.SkeletonUtils.retargetClip(destmesh, sourcemesh, newclip, {useFirstFramePosition: true});
419
+ //console.log('retarget it!', newclip, destmesh, sourcemesh);
420
+ newclip.tracks.forEach(track => {
421
+ let [name, property] = track.name.split('.');
422
+ let srcbone = sourcemesh.skeleton.getBoneByName(name);
423
+ let dstbone = destmesh.skeleton.getBoneByName(name);
424
+ //console.log(' - fix track', name, property, track, srcbone, dstbone);
425
+ if (dstbone) {
426
+ let scale = srcbone.position.length() / dstbone.position.length();
427
+ if (property == 'position') {
428
+ for (let i = 0; i < track.values.length; i++) {
429
+ track.values[i] /= scale;
430
+ }
431
+ //remove.push(track);
432
+ } else if (property == 'quaternion') {
433
+ }
434
+ } else {
435
+ //console.log('missing bone!', srcbone, track);
436
+ remove.push(track);
437
+ }
438
+ });
439
+ }
440
+ remove.forEach(track => {
441
+ let idx = newclip.tracks.indexOf(track);
442
+ if (idx != -1) {
443
+ newclip.tracks.splice(idx, 1);
444
+ //console.log('removed track', track);
445
+ }
446
+ });
447
+ //console.log('finished clip', newclip);
448
+ return newclip;
449
+ }
230
450
  this.rebindAnimations = function() {
231
451
  this.body.rebindAnimations();
232
452
  }
453
+ this.loadAnimationExtras = function() {
454
+ let animids = this.animation_extras.split(' ');
455
+ animids.forEach(animid => this.loadAnimations(animid));
456
+ }
233
457
 
234
458
  this.start = function() {
235
459
  elation.engine.things.janusghost.extendclass.start.call(this);
@@ -289,13 +513,16 @@ elation.require(['janusweb.janusbase', 'engine.things.leapmotion'], function() {
289
513
 
290
514
  matrix.makeBasis(xdir, ydir, zdir);
291
515
  q1.setFromRotationMatrix(matrix);
292
- this.head.properties.orientation.copy(this.orientation).inverse().multiply(q1);
516
+ this.head.properties.orientation.copy(this.orientation).invert().multiply(q1);
517
+ if (this.body && this.headaction) {
518
+ this.setHeadOrientation(this.head.orientation);
519
+ }
293
520
  if (movedata.head_pos && this.face) {
294
521
  var headpos = this.head.properties.position;
295
522
  var facepos = this.face.properties.position;
296
523
  var newpos = parser.getVectorValue(movedata.head_pos);
297
- headpos.copy(this.head_pos);
298
- facepos.fromArray(newpos).sub(headpos);
524
+ //headpos.copy(this.head_pos);
525
+ //facepos.fromArray(newpos).sub(headpos);
299
526
  if (this.body) {
300
527
  if (this.head.parent != this.body) {
301
528
  this.body.appendChild(this.head);
@@ -357,6 +584,20 @@ elation.require(['janusweb.janusbase', 'engine.things.leapmotion'], function() {
357
584
  if (movedata.userid_pos) {
358
585
  this.userid_pos = movedata.userid_pos;
359
586
  }
587
+
588
+ // FIXME - workaround for null values
589
+ if (isNaN(this.position.x)) this.position.x = 0;
590
+ if (isNaN(this.position.y)) this.position.y = 0;
591
+ if (isNaN(this.position.z)) this.position.z = 0;
592
+ if (isNaN(this.orientation.x) || isNaN(this.orientation.y) || isNaN(this.orientation.z) || isNaN(this.orientation.w)) this.orientation.set(0,0,0,1);
593
+
594
+ if (this.head) {
595
+ if (isNaN(this.head.position.x)) this.head.position.x = 0;
596
+ if (isNaN(this.head.position.y)) this.head.position.y = 0;
597
+ if (isNaN(this.head.position.z)) this.head.position.z = 0;
598
+ if (isNaN(this.head.orientation.x) || isNaN(this.head.orientation.y) || isNaN(this.head.orientation.z) || isNaN(this.head.orientation.w)) this.head.orientation.set(0,0,0,1);
599
+ }
600
+
360
601
  this.objects.dynamics.updateState();
361
602
  this.refresh();
362
603
  }
@@ -391,6 +632,14 @@ elation.require(['janusweb.janusbase', 'engine.things.leapmotion'], function() {
391
632
  this.screen_name = ghostdef.screen_name;
392
633
  }
393
634
  this.setGhostAssets(things.assets);
635
+ this.animation_extras = ghostdef.animation_extras;
636
+ this.bone_head = ghostdef.bone_head;
637
+ this.morphtarget_mouth = ghostdef.morphtarget_mouth;
638
+ this.morphtarget_eyes = ghostdef.morphtarget_eyes;
639
+ this.lighting = elation.utils.any(ghostdef.lighting, true);
640
+ if (ghostdef.morphtarget_eyes) {
641
+ this.blink();
642
+ }
394
643
  if (ghostdef.head_id) {
395
644
  this.setHead(ghostdef.head_id, headpos, ghostdef.scale);
396
645
  }
@@ -431,7 +680,7 @@ elation.require(['janusweb.janusbase', 'engine.things.leapmotion'], function() {
431
680
  }
432
681
 
433
682
  var inverse = new THREE.Matrix4();
434
- inverse.getInverse(this.objects['3d'].matrixWorld);
683
+ inverse.copy(this.objects['3d'].matrixWorld).invert();
435
684
  if (hand0 && hand0.state) {
436
685
  this.hands.left.show();
437
686
  this.hands.left.setState(hand0.state, inverse);
@@ -460,6 +709,7 @@ elation.require(['janusweb.janusbase', 'engine.things.leapmotion'], function() {
460
709
  }
461
710
  }
462
711
  this.updateTransparency = function() {
712
+ return;
463
713
  var player = this.engine.client.player;
464
714
 
465
715
  var dist = player.distanceTo(this);
@@ -478,17 +728,6 @@ elation.require(['janusweb.janusbase', 'engine.things.leapmotion'], function() {
478
728
  m.visible = (opacity > 0);
479
729
  });
480
730
  }
481
- this.setRoom = function(room) {
482
- if (room !== this.room) {
483
- if (this.room) {
484
- this.room.remove(this);
485
- }
486
- this.room = room;
487
- if (room && this.parent != this.room) {
488
- room.add(this);
489
- }
490
- }
491
- }
492
731
  this.setRemoteVideo = function(video) {
493
732
  if (video) {
494
733
  this.remotevideo = video;
@@ -540,5 +779,64 @@ elation.require(['janusweb.janusbase', 'engine.things.leapmotion'], function() {
540
779
  });
541
780
  }
542
781
  }
782
+ this.setAnimation = function(anim) {
783
+ if (this.body) this.body.anim_id = anim;
784
+ }
785
+ this.initHeadAnimation = function(animasset) {
786
+ let body = this.body || this._target.body,
787
+ head = this.head || this._target.head;
788
+ if (body && this.bone_head && !this.headaction) {
789
+ if (!body.animationmixer) {
790
+ body.initAnimations([]);
791
+ }
792
+ // Set up our animation mixer with a simple bone mapper for our head. We'll add more animations to this as other assets load
793
+ // TODO - this is probably also where we'd map any other tracked objects (hands, hips, etc). and set up IK
794
+
795
+ let headtrack = new THREE.QuaternionKeyframeTrack(this.bone_head + '.quaternion', [0], [0, 0, 0, 1]),
796
+ headclip = new THREE.AnimationClip('head_rotation', -1, [headtrack]);
797
+ let headaction = body.animationmixer.clipAction(this.retargetAnimation(headclip, animasset._model));
798
+ headaction.weight = 5;
799
+ this.headaction = headaction;
800
+ headaction.play();
801
+ }
802
+ }
803
+ this.setHeadOrientation = function(orientation, invert) {
804
+ let headaction = this.headaction || this._target.headaction;
805
+ if (headaction && headaction._clip.tracks[0]) {
806
+ let track = headaction._clip.tracks[0];
807
+ track.values[0] = orientation.x * (invert ? -1 : 1);
808
+ track.values[1] = orientation.y * (invert ? -1 : 1);
809
+ track.values[2] = orientation.z * (invert ? -1 : 1);
810
+ track.values[3] = orientation.w; // * (invert ? -1 : 1);
811
+ }
812
+ }
813
+ this.blink = function() {
814
+ if (!this.blinktimer) {
815
+ if (this.body && this.morphtarget_eyes) {
816
+ this.body.setMorphTargetInfluence(this.morphtarget_eyes, 1);
817
+ setTimeout(() => {
818
+ this.body.setMorphTargetInfluence(this.morphtarget_eyes, 0);
819
+ }, 100);
820
+ }
821
+ this.blinktimer = setTimeout(() => {
822
+ this.blinktimer = false;
823
+ this.blink();
824
+ }, 2500 + Math.random() * 8500);
825
+ }
826
+ }
827
+ this.setSpeakingVolume = function(volume) {
828
+ if (this.body && this.morphtarget_mouth) {
829
+ this.body.setMorphTargetInfluence(this.morphtarget_mouth, volume);
830
+ }
831
+ }
832
+ this.getProxyObject = function(classdef) {
833
+ if (!this._proxyobject) {
834
+ this._proxyobject = elation.engine.things.janusghost.extendclass.getProxyObject.call(this, classdef);
835
+ this._proxyobject._proxydefs = {
836
+ body: [ 'property', 'body'],
837
+ };
838
+ }
839
+ return this._proxyobject;
840
+ }
543
841
  }, elation.engine.things.janusbase);
544
842
  });
@@ -18,6 +18,8 @@ elation.require(['janusweb.janusbase'], function() {
18
18
  light_shadow_bias: { type: 'float', default: .0001, set: this.updateLight },
19
19
  light_shadow_radius: { type: 'float', default: 2.5, set: this.updateLight },
20
20
  light_helper: { type: 'boolean', default: false, set: this.updateLightHelper },
21
+ light_style: { type: 'string', default: '' },
22
+ light_style_fps: { type: 'float', default: 10 },
21
23
  collision_id: { type: 'string', default: 'sphere', set: this.updateCollider },
22
24
  collision_trigger: { type: 'boolean', default: true, set: this.updateCollider },
23
25
  });
@@ -98,8 +100,14 @@ elation.require(['janusweb.janusbase'], function() {
98
100
  this.updateLightTarget();
99
101
  }
100
102
  if (this.light_directional || this.light_cone_angle == 1) {
101
- this.light.position.subVectors(this.light.position, this.light.target.position).add(player.pos);
102
- this.light.target.position.copy(player.pos);
103
+ //this.light.position.subVectors(this.light.position, this.light.target.position).add(player.pos).sub(this.pos);
104
+ this.light.position.subVectors(this.position, this.light.target.position).normalize().multiplyScalar(this.light_range / 2).add(player.pos).sub(this.pos);
105
+ this.light.target.position.copy(player.pos).sub(this.pos);
106
+ }
107
+ if (this.light_style != '') {
108
+ let idx = Math.floor(Date.now() / (1000 / this.light_style_fps)) % this.light_style.length;
109
+ let brightness = (this.light_style.charCodeAt(idx) - 97) / 13;
110
+ this.light.intensity = .1 * brightness;
103
111
  }
104
112
  }
105
113
  this.createLight = function() {
@@ -127,6 +135,7 @@ elation.require(['janusweb.janusbase'], function() {
127
135
  //this.light.intensity = this.light_intensity / 100;
128
136
  var avgscale = (this.scale.x + this.scale.y + this.scale.z) / 3;
129
137
  //this.light.intensity = this.light_intensity / 100;
138
+ let brightness = 1;
130
139
  this.light.intensity = .1;
131
140
  this.light.penumbra = this.light_penumbra;
132
141
  this.light.decay = this.light_decay;
@@ -13,7 +13,7 @@ elation.require(['janusweb.janusbase'], function() {
13
13
  depth_write: { type: 'boolean', default: true },
14
14
  depth_test: { type: 'boolean', default: true },
15
15
  collision_id: { type: 'string', default: 'cube' },
16
- collision_scale: { type: 'vector3', default: V(1, 1, .02) },
16
+ collision_scale: { type: 'vector3', default: V(.5, .5, .02) },
17
17
  shadow: { type: 'boolean', default: true, set: this.updateMaterial },
18
18
  shadow_receive: { type: 'boolean', default: true, set: this.updateMaterial, comment: 'Receive shadows from self and other objects' },
19
19
  shadow_cast: { type: 'boolean', default: true, set: this.updateMaterial, comment: 'Cast shadows onto self and other objects' },
@@ -29,8 +29,10 @@ elation.require(['janusweb.janusbase'], function() {
29
29
  return mesh;
30
30
  }
31
31
  this.createForces = function() {
32
- this.setCollider('box', { min: V(-1, -1, -.01), max: V(1, 1, .01) });
32
+ this.setCollider('box', { min: V(-.8, -.8, -.01), max: V(.8, .8, .01) });
33
+ //this.collision_id = 'cube';
33
34
  }
35
+ this.updateColliderFromGeometry = function() { }
34
36
  this.createMaterial = function() {
35
37
  var texture = this.createTexture();
36
38
  var sidemap = {
@@ -66,15 +68,15 @@ elation.require(['janusweb.janusbase'], function() {
66
68
  if (!this.canvas || !this.texture) return;
67
69
  var ctx = this.canvas.getContext('2d'),
68
70
  texture = this.texture;
69
- this.canvas.width = 512;
70
- this.canvas.height = 512;
71
+ this.canvas.width = 1024;
72
+ this.canvas.height = 1024;
71
73
  var text_col = '#' + this.text_col.getHexString(),
72
74
  back_col = 'rgba(' + (this.back_col.r * 255) + ', ' + (this.back_col.g * 255) + ', ' + (this.back_col.b * 255) + ', ' + this.back_alpha + ')';
73
75
  var basestyle = 'font-family: sans-serif;' +
74
76
  'font-size: ' + this.font_size + 'px;' +
75
77
  'color: ' + text_col + ';' +
76
78
  'background: ' + back_col + ';' +
77
- 'max-width: 502px;' +
79
+ 'max-width: 1014px;' +
78
80
  'padding: 5px;';
79
81
 
80
82
  // We need to sanitize our HTML in case someone provides us with malformed markup.
@@ -115,7 +117,7 @@ elation.require(['janusweb.janusbase'], function() {
115
117
  texture.needsUpdate = true;
116
118
  this.refresh();
117
119
  }
118
- }
120
+ };
119
121
  this.currentImage = img;
120
122
  img.src = url;
121
123
  this.refresh();
@@ -39,7 +39,6 @@ elation.require(['janusweb.janusbase'], function() {
39
39
  opacity: { type: 'float', default: 1.0, set: this.updateMaterial },
40
40
  fade_in: { type: 'float', default: 1.0 },
41
41
  fade_out: { type: 'float', default: 1.0 },
42
- duration: { type: 'float', default: 1.0 },
43
42
  particle_scale: { type: 'vector3', default: [1, 1, 1], set: this.updateMaterial},
44
43
  particle_vel: { type: 'vector3', default: [0, 0, 0]},
45
44
  particle_accel: { type: 'vector3', default: [0, 0, 0]},
@@ -52,8 +51,8 @@ elation.require(['janusweb.janusbase'], function() {
52
51
  refreshrate: { type: 'int', default: 30 },
53
52
  blend_src: { type: 'string', default: 'src_alpha', set: this.updateMaterial },
54
53
  blend_dest: { type: 'string', default: 'one_minus_src_alpha', set: this.updateMaterial },
55
- depthwrite: { type: 'bool', default: false },
56
- depthtest: { type: 'bool', default: true },
54
+ depth_write: { type: 'bool', default: false },
55
+ depth_test: { type: 'bool', default: true },
57
56
  });
58
57
  this.particles = [];
59
58
  this.emitted = 0;
@@ -67,11 +66,12 @@ elation.require(['janusweb.janusbase'], function() {
67
66
  this.collidable = false;
68
67
  this.boundingRadiusSq = 0;
69
68
  this.boundingSphereWorld = new THREE.Sphere();
69
+ this.furthestPoint = new THREE.Vector3();
70
70
  this.lastrefresh = 0;
71
71
  this.updateParticles = elation.bind(this, this.updateParticles); // FIXME - hack, this should happen at the lower level of all components
72
72
  }
73
73
  this.createObject3D = function() {
74
- var geo = this.geometry = new THREE.BufferGeometry()
74
+ var geo = this.geometry = new THREE.BufferGeometry();
75
75
 
76
76
  var texture = null,
77
77
  textureasset = null;
@@ -131,8 +131,8 @@ elation.require(['janusweb.janusbase'], function() {
131
131
  //blending: THREE.AdditiveBlending,
132
132
  transparent: true,
133
133
  alphaTest: .001,
134
- depthWrite: this.depthwrite,
135
- depthTest: this.depthtest,
134
+ depthWrite: this.depth_write,
135
+ depthTest: this.depth_test,
136
136
  } );
137
137
 
138
138
  this.material = mat;
@@ -289,8 +289,8 @@ elation.require(['janusweb.janusbase'], function() {
289
289
  elapsed = now - this.lasttime,
290
290
  endtime = now + this.duration * 1000,
291
291
  emitted = 0,
292
- startpoint = this.currentpoint;
293
- spawncount = this.rate * elapsed / 1000;
292
+ startpoint = this.currentpoint,
293
+ spawncount = this.rate * elapsed / 1000,
294
294
  count = this.count,
295
295
  loop = this.loop;
296
296
 
@@ -324,11 +324,19 @@ elation.require(['janusweb.janusbase'], function() {
324
324
  // We also rate limit here, so if nothing else in the scene is changing, we
325
325
  // render at a lower fps
326
326
  this.localToWorld(this.boundingSphereWorld.center.set(0,0,0));
327
- if (!this.objects['3d'].geometry.boundingSphere) {
328
- this.objects['3d'].geometry.computeBoundingSphere();
327
+ let geo = this.objects['3d'].geometry;
328
+ if (!geo.boundingSphere) {
329
+ geo.computeBoundingSphere();
330
+ } else {
331
+ let currentBoundingRadius = geo.boundingSphere.radius;
332
+ if (this.boundingRadiusSq > currentBoundingRadius * currentBoundingRadius) {
333
+ geo.boundingSphere.radius = Math.sqrt(this.boundingRadiusSq) + .1;
334
+ }
329
335
  }
330
- this.boundingSphereWorld.radius = this.objects['3d'].geometry.boundingSphere.radius;
336
+
337
+ this.boundingSphereWorld.radius = geo.boundingSphere.radius;
331
338
  if (player.viewfrustum.intersectsSphere(this.boundingSphereWorld) && now - this.lastrefresh > (1000 / this.refreshrate)) {
339
+
332
340
  this.refresh();
333
341
  this.lastrefresh = now;
334
342
  }
@@ -341,11 +349,14 @@ elation.require(['janusweb.janusbase'], function() {
341
349
  var lengthSq = vec.lengthSq();
342
350
  if (lengthSq > this.boundingRadiusSq) {
343
351
  this.boundingRadiusSq = lengthSq;
352
+ this.furthestPoint.copy(vec);
353
+ /*
344
354
  var geo = this.objects['3d'].geometry;
345
355
  if (!geo.boundingSphere) {
346
356
  geo.boundingSphere = new THREE.Sphere();
347
357
  }
348
358
  geo.boundingSphere.radius = Math.sqrt(lengthSq);
359
+ */
349
360
  }
350
361
  }
351
362
  }
@@ -396,7 +407,7 @@ elation.require(['janusweb.janusbase'], function() {
396
407
  var randomInRange = function(range) {
397
408
  //return (Math.random() - .5) * range;
398
409
  return Math.random() * range;
399
- }
410
+ };
400
411
  let pointpos = point.pos;
401
412
  pointpos.set(randomInRange(rand_pos.x), randomInRange(rand_pos.y), randomInRange(rand_pos.z));
402
413
  if (this.emitpoints) {
@@ -461,8 +472,7 @@ elation.require(['janusweb.janusbase'], function() {
461
472
  mesh.traverse(function(n) {
462
473
  if (n && n.geometry) {
463
474
  var geo = n.geometry;
464
- if (geo instanceof THREE.Geometry) {
465
- } else if (geo instanceof THREE.BufferGeometry) {
475
+ if (geo instanceof THREE.BufferGeometry) {
466
476
  var positions = geo.attributes.position.array;
467
477
  for (var i = 0; i < positions.length; i += 3) {
468
478
  vertices.push(new THREE.Vector3(positions[i] * scale.x, positions[i+1] * scale.y, positions[i+2] * scale.z));
@@ -521,8 +531,8 @@ elation.require(['janusweb.janusbase'], function() {
521
531
  count: [ 'property', 'count'],
522
532
  duration: [ 'property', 'duration'],
523
533
  opacity: [ 'property', 'opacity'],
524
- depthwrite: [ 'property', 'depthwrite'],
525
- depthtest: [ 'property', 'depthtest'],
534
+ depth_write: [ 'property', 'depth_write'],
535
+ depth_test: [ 'property', 'depth_test'],
526
536
  play: [ 'function', 'start'],
527
537
  };
528
538
  }