From 79e4aa76a650c011d93755b63a2ed44fd832d777 Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Thu, 22 Jul 2021 12:55:57 -0500 Subject: [PATCH 01/19] use greenscreen shader in webcam avatars --- src/components/video-texture-target.js | 64 ++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/src/components/video-texture-target.js b/src/components/video-texture-target.js index 2496347f2a..cc61357db3 100644 --- a/src/components/video-texture-target.js +++ b/src/components/video-texture-target.js @@ -1,6 +1,28 @@ import { disposeTexture } from "../utils/material-utils"; import { createVideoOrAudioEl } from "../utils/media-utils"; +const vertexShader = [ + "varying vec2 vUv;", + "void main(void)", + "{", + "vUv = uv;", + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + "gl_Position = projectionMatrix * mvPosition;", + "}" +].join("\n"); + +const fragmentShader = [ + "uniform sampler2D map;", + "uniform vec3 chroma;", + "varying vec2 vUv;", + "void main(void)", + "{", + "vec3 tColor = texture( map, vUv ).rgb;", + "float a = (length(tColor - chroma) - 0.5) * 7.0;", + "gl_FragColor = vec4(tColor, a);", + "}" +].join("\n"); + /** * @component video-texture-target * This component is intended to be used on entities with a mesh/skinned mesh Object3D @@ -23,6 +45,16 @@ AFRAME.registerComponent("video-texture-target", { ); }, + setMaterial(material) { + if (this.el.object3DMap.skinnedmesh) { + this.el.object3DMap.skinnedmesh.material = material; + } else if (this.el.object3DMap.mesh) { + this.el.object3DMap.mesh.material = material; + } else { + this.el.object3D.material = material; + } + }, + init() { const material = this.getMaterial(); @@ -72,15 +104,29 @@ AFRAME.registerComponent("video-texture-target", { texture.wrapT = originalTexture.wrapT; } - if (this.data.targetBaseColorMap) { - material.map = texture; - } - - if (this.data.targetEmissiveMap) { - material.emissiveMap = texture; - } - - material.needsUpdate = true; + const newMaterial = new THREE.ShaderMaterial({ + uniforms: { + chroma: { + value: { x: 0.1, y: 0.9, z: 0.2 } + }, + map: { + value: texture + } + }, + vertexShader, + fragmentShader, + transparent: true + }); + + // if (this.data.targetBaseColorMap) { + // material.map = texture; + // } + + // if (this.data.targetEmissiveMap) { + // material.emissiveMap = texture; + // } + this.setMaterial(newMaterial); + // material.needsUpdate = true; }); } else { if (material.map && material.map !== this.originalMap) { From bd4ffcefbf97dab59f9837320276cedc92738e16 Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Fri, 23 Jul 2021 13:56:28 -0500 Subject: [PATCH 02/19] change chroma key to black --- src/components/video-texture-target.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/video-texture-target.js b/src/components/video-texture-target.js index cc61357db3..5105c1140a 100644 --- a/src/components/video-texture-target.js +++ b/src/components/video-texture-target.js @@ -107,7 +107,7 @@ AFRAME.registerComponent("video-texture-target", { const newMaterial = new THREE.ShaderMaterial({ uniforms: { chroma: { - value: { x: 0.1, y: 0.9, z: 0.2 } + value: { x: 0, y: 0, z: 0 } }, map: { value: texture From 92fc033e63f14b9ced6521ab32c553ce47dea8c1 Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Sat, 24 Jul 2021 13:46:22 -0500 Subject: [PATCH 03/19] Model animation sync system --- src/components/unseen/animation-sync.js | 46 +++++++++++++++++++++++++ src/hub.js | 1 + src/message-dispatch.js | 3 ++ 3 files changed, 50 insertions(+) create mode 100644 src/components/unseen/animation-sync.js diff --git a/src/components/unseen/animation-sync.js b/src/components/unseen/animation-sync.js new file mode 100644 index 0000000000..cfe6462fb5 --- /dev/null +++ b/src/components/unseen/animation-sync.js @@ -0,0 +1,46 @@ +export const UNSEEN_EVENT = "unseen::animation"; +export const UNSEEN_CHANNEL = "unseen::animation"; + +const timings = { + bloom: { + start: 5, + duration: 1000 + } +}; + +window.AFRAME.registerSystem("unseen-animation-sync", { + init() { + if (window.NAF.connection.isConnected()) { + this.setupNetwork(); + } else { + document.body.addEventListener("connected", () => this.setupNetwork(), { once: true }); + } + this.sceneEl.addEventListener(UNSEEN_EVENT, event => this.triggerAnimation(event)); + }, + triggerAnimation(event) { + this.doAnimation(event.detail); + if (window.NAF.connection.isConnected()) { + window.NAF.connection.broadcastDataGuaranteed(UNSEEN_CHANNEL, event.detail); + } + }, + doAnimation(data) { + console.log("doAnimation", data); + for (const piece of document.querySelectorAll(`.${data.modelName}`)) { + if (piece.components["animation-mixer"]) { + const startTime = timings[data.animationName].start; + const elapsedTime = timings[data.animationName].duration; + piece.components["animation-mixer"].mixer._actions[0].paused = false; + piece.components["animation-mixer"].mixer.setTime(startTime); + setTimeout(() => { + piece.components["animation-mixer"].mixer.setTime(0); + piece.components["animation-mixer"].mixer._actions[0].paused = true; + }, elapsedTime); + } + } + }, + setupNetwork() { + window.NAF.connection.subscribeToDataChannel(UNSEEN_CHANNEL, (_, dataType, data) => { + this.doAnimation(data); + }); + } +}); diff --git a/src/hub.js b/src/hub.js index 15eeb462bc..2e0cbbc9e3 100644 --- a/src/hub.js +++ b/src/hub.js @@ -161,6 +161,7 @@ import "./systems/capture-system"; import "./systems/listed-media"; import "./systems/linked-media"; import { SOUND_CHAT_MESSAGE } from "./systems/sound-effects-system"; +import "./components/unseen/animation-sync"; import "./gltf-component-mappings"; diff --git a/src/message-dispatch.js b/src/message-dispatch.js index 1da3bc008e..c85360d5cd 100644 --- a/src/message-dispatch.js +++ b/src/message-dispatch.js @@ -182,6 +182,9 @@ export default class MessageDispatch extends EventTarget { } } break; + case "bloom": + this.scene.emit("unseen::animation", { animationName: "bloom", modelName: "flower" }); + break; } }; } From 56b460a1b625c997ae84914992b422e05941dcb0 Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Sat, 24 Jul 2021 15:23:49 -0500 Subject: [PATCH 04/19] bloom action keybinding --- src/systems/character-controller-system.js | 3 +++ src/systems/userinput/bindings/keyboard-mouse-user.js | 5 +++++ src/systems/userinput/paths.js | 2 ++ 3 files changed, 10 insertions(+) diff --git a/src/systems/character-controller-system.js b/src/systems/character-controller-system.js index 5f720e9f6a..842d7edad4 100644 --- a/src/systems/character-controller-system.js +++ b/src/systems/character-controller-system.js @@ -231,6 +231,9 @@ export class CharacterControllerSystem { } const userinput = AFRAME.scenes[0].systems.userinput; + if (userinput.get(paths.actions.unseenBloom)) { + this.avatarRig.messageDispatch.dispatch("/bloom"); + } const wasFlying = this.fly; if (userinput.get(paths.actions.toggleFly)) { this.shouldLandWhenPossible = false; diff --git a/src/systems/userinput/bindings/keyboard-mouse-user.js b/src/systems/userinput/bindings/keyboard-mouse-user.js index 50d5eaff6d..8065ffee04 100644 --- a/src/systems/userinput/bindings/keyboard-mouse-user.js +++ b/src/systems/userinput/bindings/keyboard-mouse-user.js @@ -102,6 +102,11 @@ export const keyboardMouseUserBindings = addSetsToBindings({ dest: { value: paths.actions.toggleFly }, xform: xforms.rising }, + { + src: { value: paths.device.keyboard.key("[") }, + dest: { value: paths.actions.unseenBloom }, + xform: xforms.rising + }, { src: { value: paths.device.keyboard.key("`") }, dest: { value: paths.actions.toggleUI }, diff --git a/src/systems/userinput/paths.js b/src/systems/userinput/paths.js index e44107e92d..7751b0f299 100644 --- a/src/systems/userinput/paths.js +++ b/src/systems/userinput/paths.js @@ -129,6 +129,8 @@ paths.actions.spawnEmoji3 = "/actions/spawnEmoji3"; paths.actions.spawnEmoji4 = "/actions/spawnEmoji4"; paths.actions.spawnEmoji5 = "/actions/spawnEmoji5"; paths.actions.spawnEmoji6 = "/actions/spawnEmoji6"; +paths.actions.unseenBloom = "/actions/unseenBloom"; + paths.haptics = {}; paths.haptics.actuators = {}; paths.haptics.actuators.left = "/haptics/actuators/left"; From 2262fe9e6afd3145343c537ad07d96774664bbfc Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Sun, 25 Jul 2021 14:20:57 -0500 Subject: [PATCH 05/19] restore spectate mode --- src/react-components/room/RoomEntryModal.js | 2 +- src/react-components/ui-root.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/react-components/room/RoomEntryModal.js b/src/react-components/room/RoomEntryModal.js index 961bdfd3f6..11c7c31afe 100644 --- a/src/react-components/room/RoomEntryModal.js +++ b/src/react-components/room/RoomEntryModal.js @@ -89,7 +89,7 @@ export function RoomEntryModal({ )} diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index 73b8246637..55fba68324 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -832,7 +832,7 @@ class UIRoot extends Component { }} showEnterOnDevice={!this.state.waitingOnAudio && canEnter && !isMobileVR} onEnterOnDevice={() => this.attemptLink()} - showSpectate={false} + showSpectate={!this.state.waitingOnAudio} onSpectate={() => this.setState({ watching: true })} showOptions={this.props.hubChannel.canOrWillIfCreator("update_hub")} onOptions={() => { From 5c91754c55dde495c3483b45275a9e049c3e0f8b Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Sun, 25 Jul 2021 14:24:42 -0500 Subject: [PATCH 06/19] always hide nametags --- src/storage/store.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/storage/store.js b/src/storage/store.js index 0ab544980a..c26f51ed0f 100644 --- a/src/storage/store.js +++ b/src/storage/store.js @@ -244,7 +244,7 @@ export default class Store extends EventTarget { creatorAssignmentTokens: [], embedTokens: [], onLoadActions: [], - preferences: {} + preferences: { onlyShowNametagsInFreeze: true } }); this._shouldResetAvatarOnInit = false; From 61c127d422d141e21c38610d9ac351f24932c994 Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Mon, 26 Jul 2021 21:11:50 -0500 Subject: [PATCH 07/19] fix webgl1 compat for ios --- src/components/video-texture-target.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/video-texture-target.js b/src/components/video-texture-target.js index 5105c1140a..a9bbb7c022 100644 --- a/src/components/video-texture-target.js +++ b/src/components/video-texture-target.js @@ -17,7 +17,7 @@ const fragmentShader = [ "varying vec2 vUv;", "void main(void)", "{", - "vec3 tColor = texture( map, vUv ).rgb;", + "vec3 tColor = texture2D( map, vUv ).rgb;", "float a = (length(tColor - chroma) - 0.5) * 7.0;", "gl_FragColor = vec4(tColor, a);", "}" @@ -94,6 +94,7 @@ AFRAME.registerComponent("video-texture-target", { const texture = new THREE.VideoTexture(video); texture.flipY = false; texture.minFilter = THREE.LinearFilter; + texture.magFilter = THREE.LinearFilter; texture.encoding = THREE.sRGBEncoding; // Copy texture settings from the original texture so that things like texture wrap settings are applied From 2768456d801b132b035504040e55bdf3cfb09c0a Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Tue, 27 Jul 2021 21:45:14 -0500 Subject: [PATCH 08/19] apply greenscreen to webcam share because avatar cams dont work on ios, switch to discard over alpha for better interaction with transparency in environment --- src/components/media-views.js | 7 ++-- src/components/unseen/chroma-key-material.js | 41 ++++++++++++++++++++ src/components/video-texture-target.js | 8 ++-- 3 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 src/components/unseen/chroma-key-material.js diff --git a/src/components/media-views.js b/src/components/media-views.js index 0f390433c5..772daf3420 100644 --- a/src/components/media-views.js +++ b/src/components/media-views.js @@ -17,6 +17,7 @@ import { applyPersistentSync } from "../utils/permissions-utils"; import { refreshMediaMirror, getCurrentMirroredMedia } from "../utils/mirror-utils"; import { detect } from "detect-browser"; import semver from "semver"; +import { makeChromaKeyMaterial } from "./unseen/chroma-key-material"; import qsTruthy from "../utils/qs_truthy"; @@ -297,7 +298,7 @@ AFRAME.registerComponent("media-video", { this.isSnapping = false; this.videoIsLive = null; // value null until we've determined if the video is live or not. this.onSnapImageLoaded = () => (this.isSnapping = false); - + /* this.el.setAttribute("hover-menu__video", { template: "#video-hover-menu", isFlat: true }); this.el.components["hover-menu__video"].getHoverMenu().then(menu => { // If we got removed while waiting, do nothing. @@ -327,7 +328,7 @@ AFRAME.registerComponent("media-video", { this.updateHoverMenu(); this.updatePlaybackState(); }); - + */ NAF.utils .getNetworkedEntity(this.el) .then(networkedEl => { @@ -660,7 +661,7 @@ AFRAME.registerComponent("media-video", { const projection = this.data.projection; if (!this.mesh || projection !== oldData.projection) { - const material = new THREE.MeshBasicMaterial(); + const material = src.startsWith("hubs://") ? makeChromaKeyMaterial(texture) : new THREE.MeshBasicMaterial(); let geometry; diff --git a/src/components/unseen/chroma-key-material.js b/src/components/unseen/chroma-key-material.js new file mode 100644 index 0000000000..7d18abde45 --- /dev/null +++ b/src/components/unseen/chroma-key-material.js @@ -0,0 +1,41 @@ +const vertexShader = [ + "varying vec2 vUv;", + "void main(void)", + "{", + "vUv = uv;", + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + "gl_Position = projectionMatrix * mvPosition;", + "}" +].join("\n"); + +const fragmentShader = [ + "uniform sampler2D map;", + "uniform vec3 chroma;", + "varying vec2 vUv;", + "void main(void)", + "{", + "vec3 tColor = texture2D( map, vUv ).rgb;", + // "float a = (length(tColor - chroma) - 0.5) * 7.0;", + // "gl_FragColor = vec4(tColor, a);", + "float a = length(tColor - chroma);", + // 20% similar (0.2 * 1.732) + "if (a < 0.34) discard;", + "gl_FragColor = vec4(tColor, 1);", + "}" +].join("\n"); + +export function makeChromaKeyMaterial(texture) { + return new THREE.ShaderMaterial({ + uniforms: { + chroma: { + value: { x: 0, y: 0, z: 0 } + }, + map: { + value: texture + } + }, + vertexShader, + fragmentShader, + transparent: false + }); +} diff --git a/src/components/video-texture-target.js b/src/components/video-texture-target.js index a9bbb7c022..4fe395be97 100644 --- a/src/components/video-texture-target.js +++ b/src/components/video-texture-target.js @@ -18,8 +18,10 @@ const fragmentShader = [ "void main(void)", "{", "vec3 tColor = texture2D( map, vUv ).rgb;", - "float a = (length(tColor - chroma) - 0.5) * 7.0;", - "gl_FragColor = vec4(tColor, a);", + "float a = length(tColor - chroma);", + // 20% similar (0.2 * 1.732) + "if (a < 0.34) discard;", + "gl_FragColor = vec4(tColor, 1);", "}" ].join("\n"); @@ -116,7 +118,7 @@ AFRAME.registerComponent("video-texture-target", { }, vertexShader, fragmentShader, - transparent: true + transparent: false }); // if (this.data.targetBaseColorMap) { From 4615c03ab5cb079cfabb1adad99ed14eb81801f7 Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Wed, 28 Jul 2021 23:18:30 -0500 Subject: [PATCH 09/19] add remaining actions, change from animation to visibility --- src/components/unseen/animation-sync.js | 29 ++++++++----------- src/message-dispatch.js | 11 ++++++- src/systems/character-controller-system.js | 9 ++++++ .../userinput/bindings/keyboard-mouse-user.js | 15 ++++++++++ src/systems/userinput/paths.js | 3 ++ 5 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/components/unseen/animation-sync.js b/src/components/unseen/animation-sync.js index cfe6462fb5..842d6411ba 100644 --- a/src/components/unseen/animation-sync.js +++ b/src/components/unseen/animation-sync.js @@ -1,15 +1,9 @@ export const UNSEEN_EVENT = "unseen::animation"; export const UNSEEN_CHANNEL = "unseen::animation"; -const timings = { - bloom: { - start: 5, - duration: 1000 - } -}; - window.AFRAME.registerSystem("unseen-animation-sync", { init() { + this.lastAction = "closed"; if (window.NAF.connection.isConnected()) { this.setupNetwork(); } else { @@ -25,18 +19,19 @@ window.AFRAME.registerSystem("unseen-animation-sync", { }, doAnimation(data) { console.log("doAnimation", data); - for (const piece of document.querySelectorAll(`.${data.modelName}`)) { - if (piece.components["animation-mixer"]) { - const startTime = timings[data.animationName].start; - const elapsedTime = timings[data.animationName].duration; - piece.components["animation-mixer"].mixer._actions[0].paused = false; - piece.components["animation-mixer"].mixer.setTime(startTime); - setTimeout(() => { - piece.components["animation-mixer"].mixer.setTime(0); - piece.components["animation-mixer"].mixer._actions[0].paused = true; - }, elapsedTime); + if (data.action === "blur") { + for (const piece of this.sceneEl.querySelectorAll(`.blur`)) { + piece.setAttribute("visible", !piece.getAttribute("visible")); + } + } else { + for (const piece of this.sceneEl.querySelectorAll(`.${data.action}`)) { + piece.setAttribute("visible", true); + } + for (const piece of this.sceneEl.querySelectorAll(`.${this.lastAction}`)) { + piece.setAttribute("visible", false); } } + this.lastAction = data.action; }, setupNetwork() { window.NAF.connection.subscribeToDataChannel(UNSEEN_CHANNEL, (_, dataType, data) => { diff --git a/src/message-dispatch.js b/src/message-dispatch.js index c85360d5cd..83aa0fb430 100644 --- a/src/message-dispatch.js +++ b/src/message-dispatch.js @@ -183,7 +183,16 @@ export default class MessageDispatch extends EventTarget { } break; case "bloom": - this.scene.emit("unseen::animation", { animationName: "bloom", modelName: "flower" }); + this.scene.emit("unseen::animation", { action: "bloom" }); + break; + case "decay": + this.scene.emit("unseen::animation", { action: "decay" }); + break; + case "closed": + this.scene.emit("unseen::animation", { action: "closed" }); + break; + case "blur": + this.scene.emit("unseen::animation", { action: "blur" }); break; } }; diff --git a/src/systems/character-controller-system.js b/src/systems/character-controller-system.js index 842d7edad4..23864d9226 100644 --- a/src/systems/character-controller-system.js +++ b/src/systems/character-controller-system.js @@ -234,6 +234,15 @@ export class CharacterControllerSystem { if (userinput.get(paths.actions.unseenBloom)) { this.avatarRig.messageDispatch.dispatch("/bloom"); } + if (userinput.get(paths.actions.unseenDecay)) { + this.avatarRig.messageDispatch.dispatch("/decay"); + } + if (userinput.get(paths.actions.unseenClosed)) { + this.avatarRig.messageDispatch.dispatch("/closed"); + } + if (userinput.get(paths.actions.unseenBlur)) { + this.avatarRig.messageDispatch.dispatch("/blur"); + } const wasFlying = this.fly; if (userinput.get(paths.actions.toggleFly)) { this.shouldLandWhenPossible = false; diff --git a/src/systems/userinput/bindings/keyboard-mouse-user.js b/src/systems/userinput/bindings/keyboard-mouse-user.js index 8065ffee04..89cd9eb659 100644 --- a/src/systems/userinput/bindings/keyboard-mouse-user.js +++ b/src/systems/userinput/bindings/keyboard-mouse-user.js @@ -107,6 +107,21 @@ export const keyboardMouseUserBindings = addSetsToBindings({ dest: { value: paths.actions.unseenBloom }, xform: xforms.rising }, + { + src: { value: paths.device.keyboard.key("]") }, + dest: { value: paths.actions.unseenDecay }, + xform: xforms.rising + }, + { + src: { value: paths.device.keyboard.key("\\") }, + dest: { value: paths.actions.unseenClosed }, + xform: xforms.rising + }, + { + src: { value: paths.device.keyboard.key("*") }, + dest: { value: paths.actions.unseenBlur }, + xform: xforms.rising + }, { src: { value: paths.device.keyboard.key("`") }, dest: { value: paths.actions.toggleUI }, diff --git a/src/systems/userinput/paths.js b/src/systems/userinput/paths.js index 7751b0f299..d894870428 100644 --- a/src/systems/userinput/paths.js +++ b/src/systems/userinput/paths.js @@ -130,6 +130,9 @@ paths.actions.spawnEmoji4 = "/actions/spawnEmoji4"; paths.actions.spawnEmoji5 = "/actions/spawnEmoji5"; paths.actions.spawnEmoji6 = "/actions/spawnEmoji6"; paths.actions.unseenBloom = "/actions/unseenBloom"; +paths.actions.unseenDecay = "/actions/unseenDecay"; +paths.actions.unseenClosed = "/actions/unseenClosed"; +paths.actions.unseenBlur = "/actions/unseenBlur"; paths.haptics = {}; paths.haptics.actuators = {}; From 5a25d602c30eb34e6d170cdfc2281c8037a0a1ce Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Wed, 28 Jul 2021 23:54:33 -0500 Subject: [PATCH 10/19] crossfade animation on flower models --- src/components/unseen/animation-sync.js | 6 ++- .../unseen/model-relative-opacity.js | 44 +++++++++++++++++++ src/hub.js | 1 + 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 src/components/unseen/model-relative-opacity.js diff --git a/src/components/unseen/animation-sync.js b/src/components/unseen/animation-sync.js index 842d6411ba..84453945e1 100644 --- a/src/components/unseen/animation-sync.js +++ b/src/components/unseen/animation-sync.js @@ -25,10 +25,12 @@ window.AFRAME.registerSystem("unseen-animation-sync", { } } else { for (const piece of this.sceneEl.querySelectorAll(`.${data.action}`)) { - piece.setAttribute("visible", true); + window.setTimeout(() => piece.setAttribute("visible", true), 100); + piece.setAttribute("animation", "property: model-relative-opacity.opacityFactor; from: 0; to: 1; dur: 1000;"); } for (const piece of this.sceneEl.querySelectorAll(`.${this.lastAction}`)) { - piece.setAttribute("visible", false); + window.setTimeout(() => piece.setAttribute("visible", false), 1000); + piece.setAttribute("animation", "property: model-relative-opacity.opacityFactor; from: 1; to: 0; dur: 1000;"); } } this.lastAction = data.action; diff --git a/src/components/unseen/model-relative-opacity.js b/src/components/unseen/model-relative-opacity.js new file mode 100644 index 0000000000..8c4515fbfb --- /dev/null +++ b/src/components/unseen/model-relative-opacity.js @@ -0,0 +1,44 @@ +AFRAME.registerComponent("model-relative-opacity", { + schema: { opacityFactor: { default: 1.0 } }, + init: function() { + this.nodeMap = {}; + this.prepareMap.bind(this); + this.traverseMesh.bind(this); + + this.el.addEventListener("model-loaded", e => { + this.prepareMap(); + this.update(); + }); + }, + prepareMap: function() { + this.traverseMesh(node => { + this.nodeMap[node.uuid] = node.material.opacity; + }); + this.mapIsPrepared = true; + }, + update: function() { + this.traverseMesh(node => { + node.material.opacity = /*this.nodeMap[node.uuid] * */ this.data.opacityFactor; + node.material.transparent = node.material.opacity < 1.0; + node.material.needsUpdate = true; + }); + }, + remove: function() { + this.traverseMesh(node => { + node.material.opacity = 1; //this.nodeMap[node.uuid]; + node.material.transparent = node.material.opacity < 1.0; + node.material.needsUpdate = true; + }); + }, + traverseMesh: function(func) { + const mesh = this.el.object3D; + if (!mesh) { + return; + } + mesh.traverse(node => { + if (node.isMesh) { + func(node); + } + }); + } +}); diff --git a/src/hub.js b/src/hub.js index 2e0cbbc9e3..6f966dfab6 100644 --- a/src/hub.js +++ b/src/hub.js @@ -162,6 +162,7 @@ import "./systems/listed-media"; import "./systems/linked-media"; import { SOUND_CHAT_MESSAGE } from "./systems/sound-effects-system"; import "./components/unseen/animation-sync"; +import "./components/unseen/model-relative-opacity"; import "./gltf-component-mappings"; From 5940bd51b3f79a2d2afb66592842bd4cd9d4a59b Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Thu, 29 Jul 2021 17:45:45 -0500 Subject: [PATCH 11/19] sound actions --- src/components/unseen/animation-sync.js | 14 ++++++++++++++ src/message-dispatch.js | 6 ++++++ src/systems/character-controller-system.js | 6 ++++++ .../userinput/bindings/keyboard-mouse-user.js | 10 ++++++++++ src/systems/userinput/paths.js | 2 ++ 5 files changed, 38 insertions(+) diff --git a/src/components/unseen/animation-sync.js b/src/components/unseen/animation-sync.js index 84453945e1..6f453e64ed 100644 --- a/src/components/unseen/animation-sync.js +++ b/src/components/unseen/animation-sync.js @@ -23,6 +23,9 @@ window.AFRAME.registerSystem("unseen-animation-sync", { for (const piece of this.sceneEl.querySelectorAll(`.blur`)) { piece.setAttribute("visible", !piece.getAttribute("visible")); } + this.playSceneSound(`blur-sound`); + } else if (data.action === "begin" || data.action === "end") { + this.playSceneSound(`${data.action}-sound`); } else { for (const piece of this.sceneEl.querySelectorAll(`.${data.action}`)) { window.setTimeout(() => piece.setAttribute("visible", true), 100); @@ -32,6 +35,7 @@ window.AFRAME.registerSystem("unseen-animation-sync", { window.setTimeout(() => piece.setAttribute("visible", false), 1000); piece.setAttribute("animation", "property: model-relative-opacity.opacityFactor; from: 1; to: 0; dur: 1000;"); } + this.playSceneSound(`${data.action}-sound`); } this.lastAction = data.action; }, @@ -39,5 +43,15 @@ window.AFRAME.registerSystem("unseen-animation-sync", { window.NAF.connection.subscribeToDataChannel(UNSEEN_CHANNEL, (_, dataType, data) => { this.doAnimation(data); }); + }, + playSceneSound(objectName) { + try { + const mediaNode = document.querySelector("#environment-root").object3D.getObjectByName(objectName); + if (mediaNode) { + mediaNode.el.components["media-video"].video.play(); + } + } catch (err) { + console.error("Error playing sound: ", err.message); + } } }); diff --git a/src/message-dispatch.js b/src/message-dispatch.js index 83aa0fb430..d3f5189b20 100644 --- a/src/message-dispatch.js +++ b/src/message-dispatch.js @@ -194,6 +194,12 @@ export default class MessageDispatch extends EventTarget { case "blur": this.scene.emit("unseen::animation", { action: "blur" }); break; + case "begin": + this.scene.emit("unseen::animation", { action: "begin" }); + break; + case "end": + this.scene.emit("unseen::animation", { action: "end" }); + break; } }; } diff --git a/src/systems/character-controller-system.js b/src/systems/character-controller-system.js index 23864d9226..814d1a87ed 100644 --- a/src/systems/character-controller-system.js +++ b/src/systems/character-controller-system.js @@ -243,6 +243,12 @@ export class CharacterControllerSystem { if (userinput.get(paths.actions.unseenBlur)) { this.avatarRig.messageDispatch.dispatch("/blur"); } + if (userinput.get(paths.actions.unseenBegin)) { + this.avatarRig.messageDispatch.dispatch("/begin"); + } + if (userinput.get(paths.actions.unseenEnd)) { + this.avatarRig.messageDispatch.dispatch("/end"); + } const wasFlying = this.fly; if (userinput.get(paths.actions.toggleFly)) { this.shouldLandWhenPossible = false; diff --git a/src/systems/userinput/bindings/keyboard-mouse-user.js b/src/systems/userinput/bindings/keyboard-mouse-user.js index 89cd9eb659..ee179172fb 100644 --- a/src/systems/userinput/bindings/keyboard-mouse-user.js +++ b/src/systems/userinput/bindings/keyboard-mouse-user.js @@ -122,6 +122,16 @@ export const keyboardMouseUserBindings = addSetsToBindings({ dest: { value: paths.actions.unseenBlur }, xform: xforms.rising }, + { + src: { value: paths.device.keyboard.key("b") }, + dest: { value: paths.actions.unseenBegin }, + xform: xforms.rising + }, + { + src: { value: paths.device.keyboard.key("n") }, + dest: { value: paths.actions.unseenEnd }, + xform: xforms.rising + }, { src: { value: paths.device.keyboard.key("`") }, dest: { value: paths.actions.toggleUI }, diff --git a/src/systems/userinput/paths.js b/src/systems/userinput/paths.js index d894870428..74434fa69b 100644 --- a/src/systems/userinput/paths.js +++ b/src/systems/userinput/paths.js @@ -133,6 +133,8 @@ paths.actions.unseenBloom = "/actions/unseenBloom"; paths.actions.unseenDecay = "/actions/unseenDecay"; paths.actions.unseenClosed = "/actions/unseenClosed"; paths.actions.unseenBlur = "/actions/unseenBlur"; +paths.actions.unseenBegin = "/actions/unseenBegin"; +paths.actions.unseenEnd = "/actions/unseenEnd"; paths.haptics = {}; paths.haptics.actuators = {}; From 33231a4fc145c9b90f75cecfc30ceb441a6e8b6e Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Fri, 27 Aug 2021 11:59:42 -0500 Subject: [PATCH 12/19] fix flower transparency issues --- src/components/unseen/animation-sync.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/components/unseen/animation-sync.js b/src/components/unseen/animation-sync.js index 6f453e64ed..16515f475a 100644 --- a/src/components/unseen/animation-sync.js +++ b/src/components/unseen/animation-sync.js @@ -1,6 +1,8 @@ export const UNSEEN_EVENT = "unseen::animation"; export const UNSEEN_CHANNEL = "unseen::animation"; +const FLOWER_ACTIONS = ["decay2", "decay", "closed", "bloom", "bloom2"]; + window.AFRAME.registerSystem("unseen-animation-sync", { init() { this.lastAction = "closed"; @@ -9,6 +11,18 @@ window.AFRAME.registerSystem("unseen-animation-sync", { } else { document.body.addEventListener("connected", () => this.setupNetwork(), { once: true }); } + if (this.sceneEl.is("loaded")) { + this.onSceneLoaded(); + } else { + const sceneStateListener = ({ detail }) => { + if (detail === "loaded") { + this.onSceneLoaded(); + this.sceneEl.removeEventListener("stateadded", sceneStateListener); + } + }; + this.sceneEl.addEventListener("stateadded", sceneStateListener); + } + this.sceneEl.addEventListener(UNSEEN_EVENT, event => this.triggerAnimation(event)); }, triggerAnimation(event) { @@ -53,5 +67,16 @@ window.AFRAME.registerSystem("unseen-animation-sync", { } catch (err) { console.error("Error playing sound: ", err.message); } + }, + onSceneLoaded() { + // draw flowers before cube to avoid transparency issues + const allFlowers = FLOWER_ACTIONS.map(flower => `.${flower}`).join(", "); + for (const flower of this.sceneEl.querySelectorAll(allFlowers)) { + flower.object3D.traverse(o => { + if (o.isMesh) { + o.renderOrder = -1; + } + }); + } } }); From 288184e37750ac440462e88c61ea6ff4a33f129f Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Fri, 27 Aug 2021 15:54:24 -0500 Subject: [PATCH 13/19] animation fixes and stages --- src/components/unseen/animation-sync.js | 55 ++++++++++++++++++++----- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/src/components/unseen/animation-sync.js b/src/components/unseen/animation-sync.js index 16515f475a..a851b84151 100644 --- a/src/components/unseen/animation-sync.js +++ b/src/components/unseen/animation-sync.js @@ -6,6 +6,7 @@ const FLOWER_ACTIONS = ["decay2", "decay", "closed", "bloom", "bloom2"]; window.AFRAME.registerSystem("unseen-animation-sync", { init() { this.lastAction = "closed"; + this.flowerState = 2; if (window.NAF.connection.isConnected()) { this.setupNetwork(); } else { @@ -33,25 +34,57 @@ window.AFRAME.registerSystem("unseen-animation-sync", { }, doAnimation(data) { console.log("doAnimation", data); + this.playSceneSound(`${data.action}-sound`); + // blur is an independent toggle if (data.action === "blur") { for (const piece of this.sceneEl.querySelectorAll(`.blur`)) { piece.setAttribute("visible", !piece.getAttribute("visible")); } - this.playSceneSound(`blur-sound`); - } else if (data.action === "begin" || data.action === "end") { - this.playSceneSound(`${data.action}-sound`); - } else { - for (const piece of this.sceneEl.querySelectorAll(`.${data.action}`)) { - window.setTimeout(() => piece.setAttribute("visible", true), 100); - piece.setAttribute("animation", "property: model-relative-opacity.opacityFactor; from: 0; to: 1; dur: 1000;"); - } - for (const piece of this.sceneEl.querySelectorAll(`.${this.lastAction}`)) { + return; + } + let newFlowerState = this.flowerState; + let toHide; + switch (data.action) { + case "bloom": + newFlowerState = Math.min(this.flowerState + 1, FLOWER_ACTIONS.length - 1); + data.action = FLOWER_ACTIONS[newFlowerState]; + toHide = FLOWER_ACTIONS[this.flowerState]; + break; + case "decay": + newFlowerState = Math.max(this.flowerState - 1, 0); + data.action = FLOWER_ACTIONS[newFlowerState]; + toHide = FLOWER_ACTIONS[this.flowerState]; + break; + case "begin": + case "closed": + data.action = "closed"; + newFlowerState = 2; + toHide = FLOWER_ACTIONS[this.flowerState]; + break; + case "end": + newFlowerState = -1; + toHide = FLOWER_ACTIONS[this.flowerState]; + } + + if (data.action === this.lastAction) { + // ignore repeats to avoid disappearing the flowers + return; + } + + for (const piece of this.sceneEl.querySelectorAll(`.${data.action}`)) { + window.setTimeout(() => piece.setAttribute("visible", true), 100); + piece.setAttribute("animation", "property: model-relative-opacity.opacityFactor; from: 0; to: 1; dur: 1000;"); + } + + if (toHide && toHide !== data.action) { + for (const piece of this.sceneEl.querySelectorAll(`.${toHide}`)) { window.setTimeout(() => piece.setAttribute("visible", false), 1000); piece.setAttribute("animation", "property: model-relative-opacity.opacityFactor; from: 1; to: 0; dur: 1000;"); } - this.playSceneSound(`${data.action}-sound`); } + this.lastAction = data.action; + this.flowerState = newFlowerState; }, setupNetwork() { window.NAF.connection.subscribeToDataChannel(UNSEEN_CHANNEL, (_, dataType, data) => { @@ -63,6 +96,8 @@ window.AFRAME.registerSystem("unseen-animation-sync", { const mediaNode = document.querySelector("#environment-root").object3D.getObjectByName(objectName); if (mediaNode) { mediaNode.el.components["media-video"].video.play(); + } else { + console.log(`No sound object found for ${objectName}`); } } catch (err) { console.error("Error playing sound: ", err.message); From a185d4f1f316e5234c891939cb906d9d87102e46 Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Mon, 30 Aug 2021 17:09:58 -0500 Subject: [PATCH 14/19] fix networking of advanced actions, fix buttom mashing breaking state --- src/components/unseen/animation-sync.js | 78 +++++++++++++++++-------- 1 file changed, 55 insertions(+), 23 deletions(-) diff --git a/src/components/unseen/animation-sync.js b/src/components/unseen/animation-sync.js index a851b84151..f421a06dad 100644 --- a/src/components/unseen/animation-sync.js +++ b/src/components/unseen/animation-sync.js @@ -2,6 +2,13 @@ export const UNSEEN_EVENT = "unseen::animation"; export const UNSEEN_CHANNEL = "unseen::animation"; const FLOWER_ACTIONS = ["decay2", "decay", "closed", "bloom", "bloom2"]; +const TRANSITION_DUR = 13000; + +/** + * hide/show performer + * id = document.querySelector('[media-frame]').getAttribute('media-frame').targetId +document.getElementById(id).object3D.visible + */ window.AFRAME.registerSystem("unseen-animation-sync", { init() { @@ -27,6 +34,9 @@ window.AFRAME.registerSystem("unseen-animation-sync", { this.sceneEl.addEventListener(UNSEEN_EVENT, event => this.triggerAnimation(event)); }, triggerAnimation(event) { + if (this.isTransitioning) { + return; + } this.doAnimation(event.detail); if (window.NAF.connection.isConnected()) { window.NAF.connection.broadcastDataGuaranteed(UNSEEN_CHANNEL, event.detail); @@ -34,9 +44,10 @@ window.AFRAME.registerSystem("unseen-animation-sync", { }, doAnimation(data) { console.log("doAnimation", data); - this.playSceneSound(`${data.action}-sound`); + let action = data.action; + this.playSceneSound(`${action}-sound`); // blur is an independent toggle - if (data.action === "blur") { + if (action === "blur") { for (const piece of this.sceneEl.querySelectorAll(`.blur`)) { piece.setAttribute("visible", !piece.getAttribute("visible")); } @@ -44,47 +55,67 @@ window.AFRAME.registerSystem("unseen-animation-sync", { } let newFlowerState = this.flowerState; let toHide; - switch (data.action) { + let transitionNeeded = false; + switch (action) { case "bloom": newFlowerState = Math.min(this.flowerState + 1, FLOWER_ACTIONS.length - 1); - data.action = FLOWER_ACTIONS[newFlowerState]; + action = FLOWER_ACTIONS[newFlowerState]; toHide = FLOWER_ACTIONS[this.flowerState]; break; case "decay": newFlowerState = Math.max(this.flowerState - 1, 0); - data.action = FLOWER_ACTIONS[newFlowerState]; + action = FLOWER_ACTIONS[newFlowerState]; toHide = FLOWER_ACTIONS[this.flowerState]; break; - case "begin": case "closed": - data.action = "closed"; newFlowerState = 2; toHide = FLOWER_ACTIONS[this.flowerState]; break; + case "begin": + action = "closed"; + // falls through case "end": - newFlowerState = -1; + newFlowerState = 2; toHide = FLOWER_ACTIONS[this.flowerState]; + // reset blur on begin/end + for (const piece of this.sceneEl.querySelectorAll(`.blur`)) { + piece.setAttribute("visible", false); + } } - if (data.action === this.lastAction) { + if (action === this.lastAction) { // ignore repeats to avoid disappearing the flowers return; } - for (const piece of this.sceneEl.querySelectorAll(`.${data.action}`)) { + for (const piece of this.sceneEl.querySelectorAll(`.${action}`)) { + transitionNeeded = true; window.setTimeout(() => piece.setAttribute("visible", true), 100); - piece.setAttribute("animation", "property: model-relative-opacity.opacityFactor; from: 0; to: 1; dur: 1000;"); + piece.setAttribute( + "animation", + `property: model-relative-opacity.opacityFactor; from: 0; to: 1; dur: ${TRANSITION_DUR}; easing: easeOutQuad;` + ); } - if (toHide && toHide !== data.action) { + if (toHide && toHide !== action) { + transitionNeeded = true; for (const piece of this.sceneEl.querySelectorAll(`.${toHide}`)) { - window.setTimeout(() => piece.setAttribute("visible", false), 1000); - piece.setAttribute("animation", "property: model-relative-opacity.opacityFactor; from: 1; to: 0; dur: 1000;"); + window.setTimeout(() => piece.setAttribute("visible", false), TRANSITION_DUR * 0.9); + piece.setAttribute( + "animation", + `property: model-relative-opacity.opacityFactor; from: 1; to: 0; dur: ${TRANSITION_DUR}; easing: easeInQuad;` + ); } } - this.lastAction = data.action; + this.lastAction = action; this.flowerState = newFlowerState; + if (transitionNeeded) { + this.isTransitioning = true; + window.setTimeout(() => { + this.isTransitioning = false; + }, TRANSITION_DUR); + } }, setupNetwork() { window.NAF.connection.subscribeToDataChannel(UNSEEN_CHANNEL, (_, dataType, data) => { @@ -105,13 +136,14 @@ window.AFRAME.registerSystem("unseen-animation-sync", { }, onSceneLoaded() { // draw flowers before cube to avoid transparency issues - const allFlowers = FLOWER_ACTIONS.map(flower => `.${flower}`).join(", "); - for (const flower of this.sceneEl.querySelectorAll(allFlowers)) { - flower.object3D.traverse(o => { - if (o.isMesh) { - o.renderOrder = -1; - } - }); - } + FLOWER_ACTIONS.map((flowerType, i) => { + for (const flower of this.sceneEl.querySelectorAll(`.${flowerType}`)) { + flower.object3D.traverse(o => { + if (o.isMesh) { + o.renderOrder = -i - 1; + } + }); + } + }); } }); From 7b04488c22befac9659360381dcba3dce3b2f05a Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Mon, 30 Aug 2021 17:25:03 -0500 Subject: [PATCH 15/19] buttery smooth slow transitions --- src/components/unseen/animation-sync.js | 27 +++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/components/unseen/animation-sync.js b/src/components/unseen/animation-sync.js index f421a06dad..0f51be57c8 100644 --- a/src/components/unseen/animation-sync.js +++ b/src/components/unseen/animation-sync.js @@ -90,21 +90,40 @@ window.AFRAME.registerSystem("unseen-animation-sync", { for (const piece of this.sceneEl.querySelectorAll(`.${action}`)) { transitionNeeded = true; - window.setTimeout(() => piece.setAttribute("visible", true), 100); + // make sure there's no flash of fully opaque flower at the start + window.setTimeout(() => piece.setAttribute("visible", true), 250); piece.setAttribute( "animation", `property: model-relative-opacity.opacityFactor; from: 0; to: 1; dur: ${TRANSITION_DUR}; easing: easeOutQuad;` ); + // updating render order to avoid sudden jumps in transition + // start drawing the outgoing flower first and then switch at the midpoint + piece.object3D.traverse(o => { + if (o.isMesh) { + o.renderOrder = -1; + window.setTimeout(() => { + o.renderOrder = -5; + }, TRANSITION_DUR / 2); + } + }); } if (toHide && toHide !== action) { transitionNeeded = true; for (const piece of this.sceneEl.querySelectorAll(`.${toHide}`)) { - window.setTimeout(() => piece.setAttribute("visible", false), TRANSITION_DUR * 0.9); + window.setTimeout(() => piece.setAttribute("visible", false), TRANSITION_DUR); piece.setAttribute( "animation", `property: model-relative-opacity.opacityFactor; from: 1; to: 0; dur: ${TRANSITION_DUR}; easing: easeInQuad;` ); + piece.object3D.traverse(o => { + if (o.isMesh) { + o.renderOrder = -5; + window.setTimeout(() => { + o.renderOrder = -1; + }, TRANSITION_DUR / 2); + } + }); } } @@ -136,11 +155,11 @@ window.AFRAME.registerSystem("unseen-animation-sync", { }, onSceneLoaded() { // draw flowers before cube to avoid transparency issues - FLOWER_ACTIONS.map((flowerType, i) => { + FLOWER_ACTIONS.map(flowerType => { for (const flower of this.sceneEl.querySelectorAll(`.${flowerType}`)) { flower.object3D.traverse(o => { if (o.isMesh) { - o.renderOrder = -i - 1; + o.renderOrder = -5; } }); } From 105fc3bbc3ecf80c850f0875ad164d11e0246e7f Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Mon, 30 Aug 2021 17:44:12 -0500 Subject: [PATCH 16/19] do sequencing logic on the sender's local so that any state desyncs are fixed on the next command --- src/components/unseen/animation-sync.js | 57 ++++++++++++++----------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/src/components/unseen/animation-sync.js b/src/components/unseen/animation-sync.js index 0f51be57c8..cb1d39c091 100644 --- a/src/components/unseen/animation-sync.js +++ b/src/components/unseen/animation-sync.js @@ -14,6 +14,8 @@ window.AFRAME.registerSystem("unseen-animation-sync", { init() { this.lastAction = "closed"; this.flowerState = 2; + this.isTransitioning = false; + this.blurState = false; if (window.NAF.connection.isConnected()) { this.setupNetwork(); } else { @@ -37,52 +39,55 @@ window.AFRAME.registerSystem("unseen-animation-sync", { if (this.isTransitioning) { return; } - this.doAnimation(event.detail); - if (window.NAF.connection.isConnected()) { - window.NAF.connection.broadcastDataGuaranteed(UNSEEN_CHANNEL, event.detail); - } - }, - doAnimation(data) { - console.log("doAnimation", data); - let action = data.action; - this.playSceneSound(`${action}-sound`); - // blur is an independent toggle - if (action === "blur") { - for (const piece of this.sceneEl.querySelectorAll(`.blur`)) { - piece.setAttribute("visible", !piece.getAttribute("visible")); - } - return; - } + let action = event.detail.action; let newFlowerState = this.flowerState; - let toHide; - let transitionNeeded = false; switch (action) { + case "blur": + if (this.blurState) { + action = "unblur"; + } + break; case "bloom": newFlowerState = Math.min(this.flowerState + 1, FLOWER_ACTIONS.length - 1); action = FLOWER_ACTIONS[newFlowerState]; - toHide = FLOWER_ACTIONS[this.flowerState]; break; case "decay": newFlowerState = Math.max(this.flowerState - 1, 0); action = FLOWER_ACTIONS[newFlowerState]; - toHide = FLOWER_ACTIONS[this.flowerState]; break; case "closed": newFlowerState = 2; - toHide = FLOWER_ACTIONS[this.flowerState]; break; case "begin": action = "closed"; // falls through case "end": newFlowerState = 2; - toHide = FLOWER_ACTIONS[this.flowerState]; - // reset blur on begin/end - for (const piece of this.sceneEl.querySelectorAll(`.blur`)) { - piece.setAttribute("visible", false); - } + } + const data = { + action, + newFlowerState, + sound: event.detail.action + }; + this.doAnimation(data); + if (window.NAF.connection.isConnected()) { + window.NAF.connection.broadcastDataGuaranteed(UNSEEN_CHANNEL, data); + } + }, + doAnimation({ action, newFlowerState, sound }) { + console.log("doAnimation", { action, newFlowerState, sound }); + let transitionNeeded = false; + this.playSceneSound(`${sound}-sound`); + // blur is an independent toggle + if (action === "blur" || action === "unblur") { + this.blurState = action === "blur"; + for (const piece of this.sceneEl.querySelectorAll(`.blur`)) { + piece.setAttribute("visible", this.blurState); + } + return; } + const toHide = FLOWER_ACTIONS[this.flowerState]; if (action === this.lastAction) { // ignore repeats to avoid disappearing the flowers return; From 5934fd72d0c763273e59358490aab0b04fd103d9 Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Mon, 30 Aug 2021 17:59:44 -0500 Subject: [PATCH 17/19] hide/show performer on begin/end --- src/components/unseen/animation-sync.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/components/unseen/animation-sync.js b/src/components/unseen/animation-sync.js index cb1d39c091..94bbe70057 100644 --- a/src/components/unseen/animation-sync.js +++ b/src/components/unseen/animation-sync.js @@ -59,10 +59,9 @@ window.AFRAME.registerSystem("unseen-animation-sync", { newFlowerState = 2; break; case "begin": - action = "closed"; - // falls through case "end": newFlowerState = 2; + break; } const data = { action, @@ -77,6 +76,7 @@ window.AFRAME.registerSystem("unseen-animation-sync", { doAnimation({ action, newFlowerState, sound }) { console.log("doAnimation", { action, newFlowerState, sound }); let transitionNeeded = false; + const performerVideo = this.getVideoEl(); this.playSceneSound(`${sound}-sound`); // blur is an independent toggle if (action === "blur" || action === "unblur") { @@ -87,6 +87,16 @@ window.AFRAME.registerSystem("unseen-animation-sync", { return; } + if (action === "begin") { + // return to initial flower state + action = "closed"; + if (performerVideo) { + performerVideo.object3D.visible = true; + } + } else if (action === "end" && performerVideo) { + performerVideo.object3D.visible = false; + } + const toHide = FLOWER_ACTIONS[this.flowerState]; if (action === this.lastAction) { // ignore repeats to avoid disappearing the flowers @@ -159,6 +169,7 @@ window.AFRAME.registerSystem("unseen-animation-sync", { } }, onSceneLoaded() { + this.performerFrameEl = document.querySelector("[media-frame]"); // draw flowers before cube to avoid transparency issues FLOWER_ACTIONS.map(flowerType => { for (const flower of this.sceneEl.querySelectorAll(`.${flowerType}`)) { @@ -169,5 +180,9 @@ window.AFRAME.registerSystem("unseen-animation-sync", { }); } }); + }, + getVideoEl() { + const id = this.performerFrameEl?.getAttribute("media-frame").targetId; + return id && document.getElementById(id); } }); From 4d4311bfa615c4d1976c3f1d1155c78349a2476a Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Mon, 30 Aug 2021 18:35:07 -0500 Subject: [PATCH 18/19] add blur shader and transition on blur commands --- src/components/unseen/animation-sync.js | 11 +++-- src/components/unseen/chroma-key-material.js | 52 ++++++++++++++------ 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/components/unseen/animation-sync.js b/src/components/unseen/animation-sync.js index 94bbe70057..65656f086d 100644 --- a/src/components/unseen/animation-sync.js +++ b/src/components/unseen/animation-sync.js @@ -79,11 +79,14 @@ window.AFRAME.registerSystem("unseen-animation-sync", { const performerVideo = this.getVideoEl(); this.playSceneSound(`${sound}-sound`); // blur is an independent toggle - if (action === "blur" || action === "unblur") { + if ((action === "blur" || action === "unblur") && performerVideo) { this.blurState = action === "blur"; - for (const piece of this.sceneEl.querySelectorAll(`.blur`)) { - piece.setAttribute("visible", this.blurState); - } + const start = this.blurState ? 0.0001 : 0.2; + const end = this.blurState ? 0.2 : 0.0001; + performerVideo.setAttribute( + "animation", + `property: components.media-video.mesh.material.uniforms.blurWidth.value; from: ${start}; to: ${end}; dur: ${TRANSITION_DUR}; easing: easeInOutQuad;` + ); return; } diff --git a/src/components/unseen/chroma-key-material.js b/src/components/unseen/chroma-key-material.js index 7d18abde45..aa14a55615 100644 --- a/src/components/unseen/chroma-key-material.js +++ b/src/components/unseen/chroma-key-material.js @@ -8,21 +8,31 @@ const vertexShader = [ "}" ].join("\n"); -const fragmentShader = [ - "uniform sampler2D map;", - "uniform vec3 chroma;", - "varying vec2 vUv;", - "void main(void)", - "{", - "vec3 tColor = texture2D( map, vUv ).rgb;", - // "float a = (length(tColor - chroma) - 0.5) * 7.0;", - // "gl_FragColor = vec4(tColor, a);", - "float a = length(tColor - chroma);", - // 20% similar (0.2 * 1.732) - "if (a < 0.34) discard;", - "gl_FragColor = vec4(tColor, 1);", - "}" -].join("\n"); +const fragmentShader = ` + uniform sampler2D map; + uniform vec3 chroma; + uniform int samples; + uniform float blurWidth; + uniform float period; + uniform float threshold; + varying vec2 vUv; + + void main(void) + { + vec3 tColor; + vec2 pos = vec2(0, 0); + for( float i=0.0;i Date: Tue, 26 Oct 2021 09:57:08 -0500 Subject: [PATCH 19/19] fix for new enforced unique names in spoke --- src/components/unseen/animation-sync.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/components/unseen/animation-sync.js b/src/components/unseen/animation-sync.js index 65656f086d..5b975a3a6c 100644 --- a/src/components/unseen/animation-sync.js +++ b/src/components/unseen/animation-sync.js @@ -1,7 +1,7 @@ export const UNSEEN_EVENT = "unseen::animation"; export const UNSEEN_CHANNEL = "unseen::animation"; -const FLOWER_ACTIONS = ["decay2", "decay", "closed", "bloom", "bloom2"]; +const FLOWER_ACTIONS = ["decay2", "decay1", "closed", "bloom1", "bloom2"]; const TRANSITION_DUR = 13000; /** @@ -106,7 +106,10 @@ window.AFRAME.registerSystem("unseen-animation-sync", { return; } - for (const piece of this.sceneEl.querySelectorAll(`.${action}`)) { + for (const piece of this.sceneEl.querySelectorAll("#environment-scene a-entity")) { + if (!piece.className.startsWith(action)) { + continue; + } transitionNeeded = true; // make sure there's no flash of fully opaque flower at the start window.setTimeout(() => piece.setAttribute("visible", true), 250); @@ -128,7 +131,10 @@ window.AFRAME.registerSystem("unseen-animation-sync", { if (toHide && toHide !== action) { transitionNeeded = true; - for (const piece of this.sceneEl.querySelectorAll(`.${toHide}`)) { + for (const piece of this.sceneEl.querySelectorAll("#environment-scene a-entity")) { + if (!piece.className.startsWith(toHide)) { + continue; + } window.setTimeout(() => piece.setAttribute("visible", false), TRANSITION_DUR); piece.setAttribute( "animation",