From 52a32aee359f2e32f77f0bd98597a501ad8a7cc2 Mon Sep 17 00:00:00 2001
From: Brian Peiris <brianpeiris@gmail.com>
Date: Thu, 1 Nov 2018 18:07:11 -0700
Subject: [PATCH] Fix avatar hover effect. Use a sweeping gradient effect

---
 src/components/hover-visuals.js     | 17 ++++++++++++----
 src/components/hoverable-visuals.js | 16 ++++++++++++++-
 src/components/media-loader.js      | 19 +++++++++++++-----
 src/components/player-info.js       |  3 ++-
 src/hub.html                        |  6 ++----
 src/utils/media-highlight-frag.glsl | 30 ++++++++++++++---------------
 src/utils/media-utils.js            |  6 +++++-
 7 files changed, 65 insertions(+), 32 deletions(-)

diff --git a/src/components/hover-visuals.js b/src/components/hover-visuals.js
index 775f51dca..0e34b061e 100644
--- a/src/components/hover-visuals.js
+++ b/src/components/hover-visuals.js
@@ -4,6 +4,10 @@
  * @component hover-visuals
  */
 AFRAME.registerComponent("hover-visuals", {
+  schema: {
+    hand: { type: "string" },
+    controller: { type: "selector" }
+  },
   init: function() {
     // uniforms are set from the component responsible for loading the mesh.
     this.uniforms = null;
@@ -12,15 +16,20 @@ AFRAME.registerComponent("hover-visuals", {
   remove() {
     this.interactorTransform = null;
   },
-  tick(time) {
+  tick() {
     if (!this.uniforms) return;
 
     this.el.object3D.matrixWorld.toArray(this.interactorTransform);
+    const hovering = this.data.controller.components["super-hands"].state.has("hover-start");
 
     for (const uniform of this.uniforms) {
-      uniform.hubs_HighlightInteractorOne.value = !!this.el.components["super-hands"].state.has("hover-start");
-      uniform.hubs_InteractorOneTransform.value = this.interactorTransform;
-      uniform.hubs_Time.value = time;
+      if (this.data.hand === "left") {
+        uniform.hubs_HighlightInteractorOne.value = hovering;
+        uniform.hubs_InteractorOneTransform.value = this.interactorTransform;
+      } else {
+        uniform.hubs_HighlightInteractorTwo.value = hovering;
+        uniform.hubs_InteractorTwoTransform.value = this.interactorTransform;
+      }
     }
   }
 });
diff --git a/src/components/hoverable-visuals.js b/src/components/hoverable-visuals.js
index 0728c95bd..9f70e7b92 100644
--- a/src/components/hoverable-visuals.js
+++ b/src/components/hoverable-visuals.js
@@ -8,12 +8,17 @@ AFRAME.registerComponent("hoverable-visuals", {
     cursorController: { type: "selector" }
   },
   init: function() {
-    // uniforms are set from the component responsible for loading the mesh.
+    // uniforms and boundingSphere are set from the component responsible for loading the mesh.
     this.uniforms = null;
+    this.boundingSphere = new THREE.Sphere();
+
     this.interactorOneTransform = [];
     this.interactorTwoTransform = [];
+    this.sweepParams = [];
   },
   remove() {
+    this.uniforms = null;
+    this.boundingBox = null;
     this.interactorOneTransform = null;
     this.interactorTwoTransform = null;
   },
@@ -42,7 +47,16 @@ AFRAME.registerComponent("hoverable-visuals", {
       interactorTwo.matrixWorld.toArray(this.interactorTwoTransform);
     }
 
+    if (interactorOne || interactorTwo) {
+      const worldY = this.el.object3D.matrixWorld.elements[13];
+      const scaledRadius = this.el.object3D.scale.y * this.boundingSphere.radius;
+      this.sweepParams[0] = worldY - scaledRadius;
+      this.sweepParams[1] = worldY + scaledRadius;
+    }
+
     for (const uniform of this.uniforms) {
+      uniform.hubs_EnableSweepingEffect.value = true;
+      uniform.hubs_SweepParams.value = this.sweepParams;
       uniform.hubs_HighlightInteractorOne.value = !!interactorOne;
       uniform.hubs_InteractorOneTransform.value = this.interactorOneTransform;
       uniform.hubs_HighlightInteractorTwo.value = !!interactorTwo;
diff --git a/src/components/media-loader.js b/src/components/media-loader.js
index aac091576..6545ca9f7 100644
--- a/src/components/media-loader.js
+++ b/src/components/media-loader.js
@@ -19,6 +19,8 @@ const fetchMaxContentIndex = url => {
   return fetch(url).then(r => parseInt(r.headers.get("x-max-content-index")));
 };
 
+const boundingBox = new THREE.Box3();
+
 AFRAME.registerComponent("media-loader", {
   schema: {
     src: { type: "string" },
@@ -102,13 +104,19 @@ AFRAME.registerComponent("media-loader", {
     delete this.showLoaderTimeout;
   },
 
-  onMediaLoaded() {
-    this.clearLoadingTimeout();
-    if (this.el.components["hoverable-visuals"]) {
-      this.el.components["hoverable-visuals"].uniforms = injectCustomShaderChunks(this.el.object3D);
+  setupHoverableVisuals() {
+    const hoverableVisuals = this.el.components["hoverable-visuals"];
+    if (hoverableVisuals) {
+      hoverableVisuals.uniforms = injectCustomShaderChunks(this.el.object3D);
+      boundingBox.setFromObject(this.el.object3DMap.mesh);
+      boundingBox.getBoundingSphere(hoverableVisuals.boundingSphere);
     }
   },
 
+  onMediaLoaded() {
+    this.setupHoverableVisuals();
+  },
+
   async update(oldData) {
     try {
       const { src } = this.data;
@@ -174,9 +182,10 @@ AFRAME.registerComponent("media-loader", {
         this.el.addEventListener(
           "model-loaded",
           () => {
-            this.onMediaLoaded();
+            this.clearLoadingTimeout();
             this.hasBakedShapes = !!(this.el.body && this.el.body.shapes.length > (this.shapeAdded ? 1 : 0));
             this.setShapeAndScale(this.data.resize);
+            this.setupHoverableVisuals();
             addAnimationComponents(this.el);
           },
           { once: true }
diff --git a/src/components/player-info.js b/src/components/player-info.js
index 612386b41..7c5cbd89e 100644
--- a/src/components/player-info.js
+++ b/src/components/player-info.js
@@ -35,8 +35,9 @@ AFRAME.registerComponent("player-info", {
       modelEl.setAttribute("gltf-model-plus", "src", this.data.avatarSrc);
     }
 
+    const uniforms = injectCustomShaderChunks(this.el.object3D);
     this.el.querySelectorAll("[hover-visuals]").forEach(el => {
-      el.components["hover-visuals"].uniforms = injectCustomShaderChunks(this.el.object3D);
+      el.components["hover-visuals"].uniforms = uniforms;
     });
   }
 });
diff --git a/src/hub.html b/src/hub.html
index 462512136..b3935f397 100644
--- a/src/hub.html
+++ b/src/hub.html
@@ -377,7 +377,6 @@
                   missOpacity: 0.1;
                   curveShootingSpeed: 12;"
               haptic-feedback
-              hover-visuals
               body="type: static; shape: none;"
               mixin="controller-super-hands"
               controls-shape-offset
@@ -404,7 +403,6 @@
                   missOpacity: 0.1;
                   curveShootingSpeed: 12;"
               haptic-feedback
-              hover-visuals
               body="type: static; shape: none;"
               mixin="controller-super-hands"
               controls-shape-offset
@@ -438,11 +436,11 @@
             </template>
 
             <template data-name="LeftHand">
-              <a-entity bone-visibility></a-entity>
+              <a-entity bone-visibility hover-visuals="hand: left; controller: #player-left-controller"></a-entity>
             </template>
 
             <template data-name="RightHand">
-              <a-entity bone-visibility></a-entity>
+              <a-entity bone-visibility hover-visuals="hand: right; controller: #player-right-controller"></a-entity>
             </template>
 
           </a-entity>
diff --git a/src/utils/media-highlight-frag.glsl b/src/utils/media-highlight-frag.glsl
index e47db1ac0..c953d39ab 100644
--- a/src/utils/media-highlight-frag.glsl
+++ b/src/utils/media-highlight-frag.glsl
@@ -15,24 +15,22 @@ if (hubs_HighlightInteractorOne || hubs_HighlightInteractorTwo) {
     dist2 = distance(hubs_WorldPosition, ip);
   }
 
+  float size = hubs_SweepParams.t - hubs_SweepParams.s;
+  float line = mod(hubs_Time / 3000.0 * size, size * 2.0) + hubs_SweepParams.s - size / 2.0;
+
   float ratio = 0.0;
+  if (hubs_EnableSweepingEffect && hubs_WorldPosition.y < line) {
+    // Highlight with an sweeping gradient
+    ratio = max(0.0, 1.0 - (line - hubs_WorldPosition.y) / size * 3.0);
+  }
+
   float pulse = sin(hubs_Time / 1000.0) + 1.0;
-  float spacing = 0.5;
-  float line = spacing * pulse - spacing / 2.0;
-  float lineWidth= 0.01;
-  float mody = mod(hubs_WorldPosition.y, spacing);
-
-  if (-lineWidth + line < mody && mody < lineWidth + line) {
-    // Highlight with an animated line effect
-    ratio = 0.5;
-  } else {
-    // Highlight with a gradient falling off with distance.
-    if (hubs_HighlightInteractorOne) {
-      ratio = -min(1.0, pow(dist1 * (9.0 + 3.0 * pulse), 3.0)) + 1.0;
-    } 
-    if (hubs_HighlightInteractorTwo) {
-      ratio += -min(1.0, pow(dist2 * (9.0 + 3.0 * pulse), 3.0)) + 1.0;
-    }
+  // Highlight with a gradient falling off with distance.
+  if (hubs_HighlightInteractorOne) {
+    ratio += -min(1.0, pow(dist1 * (9.0 + 3.0 * pulse), 3.0)) + 1.0;
+  } 
+  if (hubs_HighlightInteractorTwo) {
+    ratio += -min(1.0, pow(dist2 * (9.0 + 3.0 * pulse), 3.0)) + 1.0;
   }
 
   ratio = min(1.0, ratio);
diff --git a/src/utils/media-utils.js b/src/utils/media-utils.js
index c192c1110..7db272e7e 100644
--- a/src/utils/media-utils.js
+++ b/src/utils/media-utils.js
@@ -140,7 +140,7 @@ export const addMedia = (src, template, contentOrigin, resolve = false, resize =
 };
 
 export function injectCustomShaderChunks(obj) {
-  const vertexRegex = /\bbegin_vertex\b/;
+  const vertexRegex = /\bskinning_vertex\b/;
   const fragRegex = /\bgl_FragColor\b/;
 
   const materialsSeen = new Set();
@@ -157,6 +157,8 @@ export function injectCustomShaderChunks(obj) {
       shader.uniforms.hubs_InteractorOneTransform = { value: [] };
       shader.uniforms.hubs_InteractorTwoTransform = { value: [] };
       shader.uniforms.hubs_InteractorTwoPos = { value: [] };
+      shader.uniforms.hubs_EnableSweepingEffect = { value: false };
+      shader.uniforms.hubs_SweepParams = { value: [] };
       shader.uniforms.hubs_HighlightInteractorOne = { value: false };
       shader.uniforms.hubs_HighlightInteractorTwo = { value: false };
       shader.uniforms.hubs_Time = { value: 0 };
@@ -182,6 +184,8 @@ export function injectCustomShaderChunks(obj) {
       const findex = flines.findIndex(line => fragRegex.test(line));
       flines.splice(findex + 1, 0, mediaHighlightFrag);
       flines.unshift("varying vec3 hubs_WorldPosition;");
+      flines.unshift("uniform bool hubs_EnableSweepingEffect;");
+      flines.unshift("uniform vec2 hubs_SweepParams;");
       flines.unshift("uniform bool hubs_HighlightInteractorOne;");
       flines.unshift("uniform mat4 hubs_InteractorOneTransform;");
       flines.unshift("uniform bool hubs_HighlightInteractorTwo;");
-- 
GitLab