From b52ecf4b46cc24a847bb209cc079ac8ff80007d6 Mon Sep 17 00:00:00 2001
From: Brian Peiris <brianpeiris@gmail.com>
Date: Tue, 30 Oct 2018 22:03:57 -0700
Subject: [PATCH] Move shader injection to media utils. Attempt to add hover
 effect to avatar hands

---
 src/components/hover-visuals.js               | 26 +++++++
 .../{interactables => }/hoverable-visuals.js  |  8 +--
 src/components/media-loader.js                | 68 ++-----------------
 src/components/player-info.js                 |  6 ++
 src/hub.html                                  |  2 +
 src/hub.js                                    |  3 +-
 .../media-highlight-frag.glsl                 |  0
 src/utils/media-utils.js                      | 63 +++++++++++++++++
 8 files changed, 107 insertions(+), 69 deletions(-)
 create mode 100644 src/components/hover-visuals.js
 rename src/components/{interactables => }/hoverable-visuals.js (89%)
 rename src/{components => utils}/media-highlight-frag.glsl (100%)

diff --git a/src/components/hover-visuals.js b/src/components/hover-visuals.js
new file mode 100644
index 000000000..775f51dca
--- /dev/null
+++ b/src/components/hover-visuals.js
@@ -0,0 +1,26 @@
+/**
+ * Listens for hoverable state changes and applies a visual effect to an entity
+ * @namespace interactables
+ * @component hover-visuals
+ */
+AFRAME.registerComponent("hover-visuals", {
+  init: function() {
+    // uniforms are set from the component responsible for loading the mesh.
+    this.uniforms = null;
+    this.interactorTransform = [];
+  },
+  remove() {
+    this.interactorTransform = null;
+  },
+  tick(time) {
+    if (!this.uniforms) return;
+
+    this.el.object3D.matrixWorld.toArray(this.interactorTransform);
+
+    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;
+    }
+  }
+});
diff --git a/src/components/interactables/hoverable-visuals.js b/src/components/hoverable-visuals.js
similarity index 89%
rename from src/components/interactables/hoverable-visuals.js
rename to src/components/hoverable-visuals.js
index 83a437573..0728c95bd 100644
--- a/src/components/interactables/hoverable-visuals.js
+++ b/src/components/hoverable-visuals.js
@@ -8,6 +8,8 @@ AFRAME.registerComponent("hoverable-visuals", {
     cursorController: { type: "selector" }
   },
   init: function() {
+    // uniforms are set from the component responsible for loading the mesh.
+    this.uniforms = null;
     this.interactorOneTransform = [];
     this.interactorTwoTransform = [];
   },
@@ -16,9 +18,7 @@ AFRAME.registerComponent("hoverable-visuals", {
     this.interactorTwoTransform = null;
   },
   tick(time) {
-    const uniforms = this.el.components["media-loader"].shaderUniforms;
-
-    if (!uniforms) return;
+    if (!this.uniforms) return;
 
     const { hoverers } = this.el.components["hoverable"];
 
@@ -42,7 +42,7 @@ AFRAME.registerComponent("hoverable-visuals", {
       interactorTwo.matrixWorld.toArray(this.interactorTwoTransform);
     }
 
-    for (const uniform of uniforms) {
+    for (const uniform of this.uniforms) {
       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 e9393c742..aac091576 100644
--- a/src/components/media-loader.js
+++ b/src/components/media-loader.js
@@ -1,7 +1,6 @@
 import { getBox, getScaleCoefficient } from "../utils/auto-box-collider";
-import { guessContentType, proxiedUrlFor, resolveUrl } from "../utils/media-utils";
+import { guessContentType, proxiedUrlFor, resolveUrl, injectCustomShaderChunks } from "../utils/media-utils";
 import { addAnimationComponents } from "../utils/animation";
-import mediaHighlightFrag from "./media-highlight-frag.glsl";
 
 import "three/examples/js/loaders/GLTFLoader";
 import loadingObjectSrc from "../assets/LoadingObject_Atom.glb";
@@ -20,67 +19,6 @@ const fetchMaxContentIndex = url => {
   return fetch(url).then(r => parseInt(r.headers.get("x-max-content-index")));
 };
 
-function injectCustomShaderChunks(obj) {
-  const vertexRegex = /\bbegin_vertex\b/;
-  const fragRegex = /\bgl_FragColor\b/;
-
-  const materialsSeen = new Set();
-  const shaderUniforms = [];
-
-  obj.traverse(object => {
-    if (!object.material || !["MeshStandardMaterial", "MeshBasicMaterial"].includes(object.material.type)) {
-      return;
-    }
-    object.material = object.material.clone();
-    object.material.onBeforeCompile = shader => {
-      if (!vertexRegex.test(shader.vertexShader)) return;
-
-      shader.uniforms.hubs_InteractorOneTransform = { value: [] };
-      shader.uniforms.hubs_InteractorTwoTransform = { value: [] };
-      shader.uniforms.hubs_InteractorTwoPos = { value: [] };
-      shader.uniforms.hubs_HighlightInteractorOne = { value: false };
-      shader.uniforms.hubs_HighlightInteractorTwo = { value: false };
-      shader.uniforms.hubs_Time = { value: 0 };
-
-      const vchunk = `
-        if (hubs_HighlightInteractorOne || hubs_HighlightInteractorTwo) {
-          vec4 wt = modelMatrix * vec4(transformed, 1);
-
-          // Used in the fragment shader below.
-          hubs_WorldPosition = wt.xyz;
-        }
-      `;
-
-      const vlines = shader.vertexShader.split("\n");
-      const vindex = vlines.findIndex(line => vertexRegex.test(line));
-      vlines.splice(vindex + 1, 0, vchunk);
-      vlines.unshift("varying vec3 hubs_WorldPosition;");
-      vlines.unshift("uniform bool hubs_HighlightInteractorOne;");
-      vlines.unshift("uniform bool hubs_HighlightInteractorTwo;");
-      shader.vertexShader = vlines.join("\n");
-
-      const flines = shader.fragmentShader.split("\n");
-      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_HighlightInteractorOne;");
-      flines.unshift("uniform mat4 hubs_InteractorOneTransform;");
-      flines.unshift("uniform bool hubs_HighlightInteractorTwo;");
-      flines.unshift("uniform mat4 hubs_InteractorTwoTransform;");
-      flines.unshift("uniform float hubs_Time;");
-      shader.fragmentShader = flines.join("\n");
-
-      if (!materialsSeen.has(object.material.uuid)) {
-        shaderUniforms.push(shader.uniforms);
-        materialsSeen.add(object.material.uuid);
-      }
-    };
-    object.material.needsUpdate = true;
-  });
-
-  return shaderUniforms;
-}
-
 AFRAME.registerComponent("media-loader", {
   schema: {
     src: { type: "string" },
@@ -166,7 +104,9 @@ AFRAME.registerComponent("media-loader", {
 
   onMediaLoaded() {
     this.clearLoadingTimeout();
-    this.shaderUniforms = injectCustomShaderChunks(this.el.object3D);
+    if (this.el.components["hoverable-visuals"]) {
+      this.el.components["hoverable-visuals"].uniforms = injectCustomShaderChunks(this.el.object3D);
+    }
   },
 
   async update(oldData) {
diff --git a/src/components/player-info.js b/src/components/player-info.js
index a7e0812f8..612386b41 100644
--- a/src/components/player-info.js
+++ b/src/components/player-info.js
@@ -1,3 +1,5 @@
+import { injectCustomShaderChunks } from "../utils/media-utils";
+
 /**
  * Sets player info state, including avatar choice and display name.
  * @namespace avatar
@@ -32,5 +34,9 @@ AFRAME.registerComponent("player-info", {
     if (this.data.avatarSrc && modelEl) {
       modelEl.setAttribute("gltf-model-plus", "src", this.data.avatarSrc);
     }
+
+    this.el.querySelectorAll("[hover-visuals]").forEach(el => {
+      el.components["hover-visuals"].uniforms = injectCustomShaderChunks(this.el.object3D);
+    });
   }
 });
diff --git a/src/hub.html b/src/hub.html
index d1fb11fed..462512136 100644
--- a/src/hub.html
+++ b/src/hub.html
@@ -377,6 +377,7 @@
                   missOpacity: 0.1;
                   curveShootingSpeed: 12;"
               haptic-feedback
+              hover-visuals
               body="type: static; shape: none;"
               mixin="controller-super-hands"
               controls-shape-offset
@@ -403,6 +404,7 @@
                   missOpacity: 0.1;
                   curveShootingSpeed: 12;"
               haptic-feedback
+              hover-visuals
               body="type: static; shape: none;"
               mixin="controller-super-hands"
               controls-shape-offset
diff --git a/src/hub.js b/src/hub.js
index 00e29df3c..70e2d528f 100644
--- a/src/hub.js
+++ b/src/hub.js
@@ -33,7 +33,8 @@ import "./components/virtual-gamepad-controls";
 import "./components/ik-controller";
 import "./components/hand-controls2";
 import "./components/character-controller";
-import "./components/interactables/hoverable-visuals";
+import "./components/hoverable-visuals";
+import "./components/hover-visuals";
 import "./components/haptic-feedback";
 import "./components/networked-video-player";
 import "./components/offset-relative-to";
diff --git a/src/components/media-highlight-frag.glsl b/src/utils/media-highlight-frag.glsl
similarity index 100%
rename from src/components/media-highlight-frag.glsl
rename to src/utils/media-highlight-frag.glsl
diff --git a/src/utils/media-utils.js b/src/utils/media-utils.js
index d72a453fa..c192c1110 100644
--- a/src/utils/media-utils.js
+++ b/src/utils/media-utils.js
@@ -1,5 +1,7 @@
 import { objectTypeForOriginAndContentType } from "../object-types";
 import { getReticulumFetchUrl } from "./phoenix-utils";
+import mediaHighlightFrag from "./media-highlight-frag.glsl";
+
 const mediaAPIEndpoint = getReticulumFetchUrl("/api/v1/media");
 
 const commonKnownContentTypes = {
@@ -136,3 +138,64 @@ export const addMedia = (src, template, contentOrigin, resolve = false, resize =
 
   return { entity, orientation };
 };
+
+export function injectCustomShaderChunks(obj) {
+  const vertexRegex = /\bbegin_vertex\b/;
+  const fragRegex = /\bgl_FragColor\b/;
+
+  const materialsSeen = new Set();
+  const shaderUniforms = [];
+
+  obj.traverse(object => {
+    if (!object.material || !["MeshStandardMaterial", "MeshBasicMaterial"].includes(object.material.type)) {
+      return;
+    }
+    object.material = object.material.clone();
+    object.material.onBeforeCompile = shader => {
+      if (!vertexRegex.test(shader.vertexShader)) return;
+
+      shader.uniforms.hubs_InteractorOneTransform = { value: [] };
+      shader.uniforms.hubs_InteractorTwoTransform = { value: [] };
+      shader.uniforms.hubs_InteractorTwoPos = { value: [] };
+      shader.uniforms.hubs_HighlightInteractorOne = { value: false };
+      shader.uniforms.hubs_HighlightInteractorTwo = { value: false };
+      shader.uniforms.hubs_Time = { value: 0 };
+
+      const vchunk = `
+        if (hubs_HighlightInteractorOne || hubs_HighlightInteractorTwo) {
+          vec4 wt = modelMatrix * vec4(transformed, 1);
+
+          // Used in the fragment shader below.
+          hubs_WorldPosition = wt.xyz;
+        }
+      `;
+
+      const vlines = shader.vertexShader.split("\n");
+      const vindex = vlines.findIndex(line => vertexRegex.test(line));
+      vlines.splice(vindex + 1, 0, vchunk);
+      vlines.unshift("varying vec3 hubs_WorldPosition;");
+      vlines.unshift("uniform bool hubs_HighlightInteractorOne;");
+      vlines.unshift("uniform bool hubs_HighlightInteractorTwo;");
+      shader.vertexShader = vlines.join("\n");
+
+      const flines = shader.fragmentShader.split("\n");
+      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_HighlightInteractorOne;");
+      flines.unshift("uniform mat4 hubs_InteractorOneTransform;");
+      flines.unshift("uniform bool hubs_HighlightInteractorTwo;");
+      flines.unshift("uniform mat4 hubs_InteractorTwoTransform;");
+      flines.unshift("uniform float hubs_Time;");
+      shader.fragmentShader = flines.join("\n");
+
+      if (!materialsSeen.has(object.material.uuid)) {
+        shaderUniforms.push(shader.uniforms);
+        materialsSeen.add(object.material.uuid);
+      }
+    };
+    object.material.needsUpdate = true;
+  });
+
+  return shaderUniforms;
+}
-- 
GitLab