summaryrefslogtreecommitdiff
path: root/public/js/components/nametag.js
diff options
context:
space:
mode:
Diffstat (limited to 'public/js/components/nametag.js')
-rw-r--r--public/js/components/nametag.js69
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);
+ }
+ },
+ });
+})();