summaryrefslogtreecommitdiff
path: root/public/js/components/nametag.js
blob: 6fb1d8707e7143576f30b2c855b222a0e90a1aa1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/* global AFRAME, THREE */
// Floating name above a remote person's head. Always faces you, shows when
// they're muted, and a dot under it glows while they talk.
(function () {
  const worldPos = new THREE.Vector3();
  const cameraPos = new THREE.Vector3();
  const parentQuat = new THREE.Quaternion();
  const yawQuat = new THREE.Quaternion();
  const UP = new THREE.Vector3(0, 1, 0);

  AFRAME.registerComponent('nametag', {
    init() {
      this.label = document.createElement('a-entity');
      this.label.setAttribute('text', {
        value: '', align: 'center', anchor: 'center', baseline: 'bottom',
        width: 1.4, wrapCount: 18, color: '#ffffff', side: 'double',
      });
      this.el.appendChild(this.label);

      this.dot = document.createElement('a-circle');
      this.dot.setAttribute('radius', 0.022);
      this.dot.setAttribute('position', '0 -0.035 0');
      this.dot.setAttribute('material', { shader: 'flat', color: '#ffb36b', transparent: true, opacity: 0.25, side: 'double' });
      this.el.appendChild(this.dot);

      this.head = this.el.parentEl;
      this.rig = this.head && this.head.parentEl;
      this.onInfo = (evt) => this.render(evt.detail);
      if (this.rig) {
        this.rig.addEventListener('player-info-changed', this.onInfo);
        const info = this.rig.getAttribute('player-info');
        if (info) this.render(info);
      }
    },

    remove() {
      if (this.rig) this.rig.removeEventListener('player-info-changed', this.onInfo);
    },

    render(info) {
      const name = (info.name || 'Someone').slice(0, 20);
      this.label.setAttribute('text', {
        value: info.muted ? `${name}\n(muted)` : name,
        color: info.muted ? '#c9c9c9' : '#ffffff',
      });
      this.dot.setAttribute('material', 'color', info.color || '#ffb36b');
    },

    tick() {
      const camera = this.el.sceneEl.camera;
      if (!camera) return;

      // Face the viewer, but stay upright however the speaker tilts their head.
      this.el.object3D.getWorldPosition(worldPos);
      camera.getWorldPosition(cameraPos);
      yawQuat.setFromAxisAngle(UP, Math.atan2(cameraPos.x - worldPos.x, cameraPos.z - worldPos.z));
      this.el.object3D.parent.getWorldQuaternion(parentQuat).invert();
      this.el.object3D.quaternion.copy(parentQuat).multiply(yawQuat);

      const voice = this.head && this.head.components['voice-level'];
      const level = voice ? voice.level : 0;
      const mesh = this.dot.getObject3D('mesh');
      if (mesh) {
        mesh.material.opacity = 0.25 + level * 0.75;
        mesh.scale.setScalar(1 + level * 0.8);
      }
    },
  });
})();