diff --git a/.babelrc b/.babelrc index 4ffef06dbfb53d6210bc68ec4218f3b3d74108fc..88f0c0ac53f9a7036d9ad906f256c6668ca51d98 100644 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,9 @@ { - "presets": ["env", "react"] + "presets": [ + "react", + ["env", { + "exclude": ["transform-regenerator"], + "useBuiltins": true + }] + ] } diff --git a/public/room.html b/public/room.html index d13ff038a2dd1a0bf8fa7b8a5f0eaa34811907fd..0b96981873dda6a51ecfb559275a31adbbc4a051 100644 --- a/public/room.html +++ b/public/room.html @@ -88,6 +88,10 @@ ></a-entity> </script> + <script id="video-template" type="text/html"> + <a-entity class="video" geometry="primitive: plane;" material="side: double" networked-video-player></a-entity> + </script> + <script id="nametag-template" type="text/html"> <a-entity class="nametag" diff --git a/src/components/audio-feedback.js b/src/components/audio-feedback.js index 2b24abd946d7d8fe2ec35c018c4262f64fd00229..4f5a5c9e53fdf54ad22b9c90dbce4024606e6555 100644 --- a/src/components/audio-feedback.js +++ b/src/components/audio-feedback.js @@ -18,7 +18,6 @@ AFRAME.registerComponent("networked-audio-analyser", { ); } const ownerId = networkedEl.components.networked.data.owner; - console.log("audio Analyser for " + ownerId); return NAF.connection.adapter.getMediaStream(ownerId); }) .then(stream => { @@ -60,11 +59,9 @@ AFRAME.registerComponent("matcolor-audio-feedback", { this.onAudioFrequencyChange = this.onAudioFrequencyChange.bind(this); this.el.addEventListener("model-loaded", () => { - console.log(this.data.objectName); this.mat = this.el.object3D.getObjectByName( this.data.objectName ).material; - console.log("mat", this.mat); }); }, diff --git a/src/components/mute-mic.js b/src/components/mute-mic.js index 12c1ad397229b512606b40e4f5cfa86637935396..272b40e3b41f5d4aa49d7107b6aa41785b8d5596 100644 --- a/src/components/mute-mic.js +++ b/src/components/mute-mic.js @@ -32,7 +32,6 @@ AFRAME.registerComponent("mute-mic", { play: function() { const { eventSrc, toggleEvents, muteEvents, unmuteEvents } = this.data; - console.log(eventSrc); bindAllEvents(eventSrc, toggleEvents, this.onToggle); bindAllEvents(eventSrc, muteEvents, this.onMute); bindAllEvents(eventSrc, unmuteEvents, this.onUnmute); diff --git a/src/components/networked-video-player.css b/src/components/networked-video-player.css new file mode 100644 index 0000000000000000000000000000000000000000..6c10dd330f8265dff0da39de02ba854b550a9e36 --- /dev/null +++ b/src/components/networked-video-player.css @@ -0,0 +1,7 @@ +:local(.video) { + position: absolute; + bottom: 0; + height: 100px; + background: black; + display: none; +} diff --git a/src/components/networked-video-player.js b/src/components/networked-video-player.js new file mode 100644 index 0000000000000000000000000000000000000000..e00dfbf15296c5d52736254706ad3ca44e73ca6f --- /dev/null +++ b/src/components/networked-video-player.js @@ -0,0 +1,52 @@ +import styles from "./networked-video-player.css"; + +const nafConnected = function() { + return new Promise(resolve => { + NAF.clientId + ? resolve() + : document.body.addEventListener("connected", resolve); + }); +}; + +AFRAME.registerComponent("networked-video-player", { + schema: {}, + async init() { + await nafConnected(); + + const networkedEl = NAF.utils.getNetworkedEntity(this.el); + if (!networkedEl) { + throw new Error( + "Video player must be added on a node, or a child of a node, with the `networked` component." + ); + } + + const ownerId = networkedEl.components.networked.data.owner; + const stream = await NAF.connection.adapter.getMediaStream(ownerId); + if (!stream) { + return; + } + + const v = document.createElement("video"); + v.classList.add(styles.video); + v.srcObject = stream; + document.body.appendChild(v); + v.play(); + + this.videoEl = v; + + v.onloadedmetadata = () => { + const ratio = v.videoWidth / v.videoHeight; + this.el.setAttribute("geometry", { + width: ratio * 1, + height: 1 + }); + this.el.setAttribute("material", "src", v); + }; + }, + + remove() { + if (this.videoEl) { + this.videoEl.parent.removeChild(this.videoEl); + } + } +}); diff --git a/src/components/offset-relative-to.js b/src/components/offset-relative-to.js new file mode 100644 index 0000000000000000000000000000000000000000..923eae3e5673488e8eceb726b9e91f592285f546 --- /dev/null +++ b/src/components/offset-relative-to.js @@ -0,0 +1,32 @@ +AFRAME.registerComponent("offset-relative-to", { + schema: { + target: { + type: "selector" + }, + offset: { + type: "vec3" + }, + on: { + type: "string" + } + }, + init() { + this.updateOffset(); + this.el.sceneEl.addEventListener( + this.data.on, + this.updateOffset.bind(this) + ); + }, + updateOffset() { + const offsetVector = new THREE.Vector3().copy(this.data.offset); + this.data.target.object3D.localToWorld(offsetVector); + this.el.setAttribute("position", offsetVector); + + const headWorldRotation = this.data.target.object3D.getWorldRotation(); + this.el.setAttribute("rotation", { + x: headWorldRotation.x * THREE.Math.RAD2DEG, + y: headWorldRotation.y * THREE.Math.RAD2DEG, + z: headWorldRotation.z * THREE.Math.RAD2DEG + }); + } +}); diff --git a/src/index.js b/src/index.js index ca84a32ebbb202586f0b3942afa8f70718941004..41760f90bd34b7b6e71b524cec3f48a7ef673d37 100644 --- a/src/index.js +++ b/src/index.js @@ -19,6 +19,8 @@ import "./components/body-controller"; import "./components/hand-controls2"; import "./components/character-controller"; import "./components/split-axis-events"; +import "./components/networked-video-player"; +import "./components/offset-relative-to"; import "./systems/personal-space-bubble"; import registerNetworkScheams from "./network-schemas"; @@ -29,8 +31,30 @@ import Config from "./config"; registerNetworkScheams(); registerInputMappings(); +function shareScreen() { + const track = NAF.connection.adapter.localMediaStream.getVideoTracks()[0]; + + const id = `${NAF.clientId}-screen`; + let entity = document.getElementById(id); + if (!entity) { + const sceneEl = document.querySelector("a-scene"); + entity = document.createElement("a-entity"); + entity.id = id; + entity.setAttribute("offset-relative-to", { + target: "#head", + offset: "0 0 -2", + on: "action_share_screen" + }); + entity.setAttribute("networked", { template: "#video-template" }); + sceneEl.appendChild(entity); + } + + track.enabled = !track.enabled; + entity.setAttribute("visible", track.enabled); +} + window.App = { - onSceneLoad() { + async onSceneLoad() { const qs = queryString.parse(location.search); const scene = document.querySelector("a-scene"); @@ -60,7 +84,25 @@ window.App = { document.body.addEventListener("connected", App.onConnect); + scene.addEventListener("action_share_screen", shareScreen); + + const mediaStream = await navigator.mediaDevices.getUserMedia({ + audio: true, + video: + qs.screen === "true" + ? { mediaSource: "screen", height: 720, frameRate: 30 } + : false + }); + + // Don't send video by deafult + const videoTracks = mediaStream.getVideoTracks(); + if (videoTracks.length) { + videoTracks[0].enabled = false; + } + scene.components["networked-scene"].connect(); + // @TODO ideally the adapter should exist before connect, but it currently doesnt so we have to do this after calling connect. This might be a race condition in other adapters. + NAF.connection.adapter.setLocalMediaStream(mediaStream); }, onConnect() { diff --git a/src/input-mappings.js b/src/input-mappings.js index b4a0dbe6dd3dfcc3eaa6238f297f8ec69c335d2b..d57ff6496b1d35bf0a435b06cfdb74dd807008e6 100644 --- a/src/input-mappings.js +++ b/src/input-mappings.js @@ -31,6 +31,7 @@ export default function registerInputMappings() { m_press: "action_mute", q_press: "action_snap_rotate_left", e_press: "action_snap_rotate_right", + v_press: "action_share_screen", w_down: "action_move_forward", w_up: "action_dont_move_forward", a_down: "action_move_left", diff --git a/src/network-schemas.js b/src/network-schemas.js index 987f0afe385a141f519621cb016e812b841b8bfa..d93c552cb6b90a68ee158c66cd93475338c60408 100644 --- a/src/network-schemas.js +++ b/src/network-schemas.js @@ -19,6 +19,11 @@ function registerNetworkSchemas() { template: "#left-hand-template", components: ["position", "rotation", "visible"] }); + + NAF.schemas.add({ + template: "#video-template", + components: ["position", "rotation", "visible"] + }); } export default registerNetworkSchemas;