diff options
Diffstat (limited to 'public/js/components/nametag.js')
| -rw-r--r-- | public/js/components/nametag.js | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/public/js/components/nametag.js b/public/js/components/nametag.js new file mode 100644 index 0000000..6fb1d87 --- /dev/null +++ b/public/js/components/nametag.js @@ -0,0 +1,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); + } + }, + }); +})(); |