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