diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000000000000000000000000000000000000..70fa045e816539292b653dad3df26c6f68b99be0 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +!.eslintrc.js +src/vendor/* diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000000000000000000000000000000000000..1cadfd103fd1910f79fb0e7928b0663b9e6a248a --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,23 @@ +module.exports = { + parser: "babel-eslint", + env: { + browser: true, + es6: true, + node: true + }, + globals: { + THREE: true, + AFRAME: true, + NAF: true + }, + plugins: ["prettier", "react"], + rules: { + "prettier/prettier": "error", + "prefer-const": "error", + "no-var": "error", + "no-throw-literal": "error", + // Light console usage is useful but remove debug logs before merging to master. + "no-console": "off" + }, + extends: ["prettier", "plugin:react/recommended", "eslint:recommended"] +}; diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index d39862227e78dc98b5d902fd0a1bf84a23fa76b4..0000000000000000000000000000000000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "parserOptions": { - "ecmaVersion": 2017, - "sourceType": "module" - }, - "plugins": [ - "prettier", - "react" - ], - "rules": { - "prettier/prettier": "error", - "prefer-const": "error", - "no-var": "error" - }, - "extends": [ - "prettier", - "plugin:react/recommended" - ] -} diff --git a/.prettierignore b/.prettierignore index edded1b3e9cda6cef1bf33b9c8e08aa12f1f84a7..4bd6eed9e8bcf23ca49b895c57f0ba6d6290e14b 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1,3 @@ package.json *.gltf +src/vendor/ diff --git a/.prettierrc.json b/.prettierrc.json index 963354f23168f2e9f79b42c261c612cc662e849c..25fc98b8834374d036ee851ffe5a75fa92ffff4a 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,3 +1,4 @@ { - "printWidth": 120 + "printWidth": 120, + "parser": "babylon" } diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..73a40d0761a7b442cd2435b7ac37a59f5c13a875 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +language: node_js +node_js: node +cache: yarn +before_install: + - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.5.1 + - export PATH="$HOME/.yarn/bin:$PATH" +install: yarn +script: yarn lint diff --git a/README.md b/README.md index 6da5b21546d11dd672925c9a7d8739dda971f49c..78e4831b0ed256a6d47b2d3b90e4737f2a42123f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Mozilla Social Mixed Reality Client +# Mozilla Social Mixed Reality Client [](https://travis-ci.org/mozilla/mr-social-client) A prototype client demonstrating a multi-user experience in WebVR. Built with [A-Frame](https://github.com/aframevr/aframe/) @@ -20,3 +20,17 @@ To bundle javascript and generate the html templates, run: ```sh yarn build ``` + +## Query Params + +- `room` - Id of the room (an integer) that you want to join +- `allow_multi` - Allow multiple instances off the app in the same browser session +- `enable_screen_sharing` - Enable screen sharing +- `accept_screen_shares` - Display screens shared by other users +- `avatar_scale` - Scale your self! +- `quality` - Either "low" or "high". Force assets to a certain quality level +- `mobile` - Force mobile mode +- `no_stats` - Disable performance stats +- `vr_entry_type` - Either "gearvr" or "daydream". Used internally to force a VR entry type + +[](http://waffle.io/mozilla/socialmr) diff --git a/package.json b/package.json index 826dabf64a323fab731294ae2afe21636325fc4c..8b69460332a771e23cbefb4bc3a2f8e033c3c37f 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "postinstall": "node ./scripts/postinstall.js", "start": "cross-env NODE_ENV=development webpack-dev-server", "build": "rimraf ./public && cross-env NODE_ENV=production webpack --mode=production", - "prettier": "prettier --write src/**/*.js" + "prettier": "prettier --write '*.js' 'src/**/*.js'", + "lint": "eslint '*.js' 'src/**/*.js'" }, "dependencies": { "@fortawesome/fontawesome": "^1.1.5", @@ -24,13 +25,10 @@ "aframe-physics-system": "https://github.com/donmccurdy/aframe-physics-system", "aframe-teleport-controls": "https://github.com/netpro2k/aframe-teleport-controls#feature/pauseable", "aframe-xr": "github:brianpeiris/aframe-xr#3162aed", - "babel-plugin-react-intl": "^2.4.0", - "babel-plugin-transform-react-jsx-img-import": "^0.1.4", "classnames": "^2.2.5", "detect-browser": "^2.1.0", "event-target-shim": "^3.0.1", "jsonschema": "^1.2.2", - "material-design-lite": "^1.3.0", "minijanus": "^0.5.0", "mobile-detect": "^1.4.1", "moving-average": "^1.0.0", @@ -49,9 +47,11 @@ }, "devDependencies": { "babel-core": "^6.26.0", - "babel-eslint": "^8.2.2", + "babel-eslint": "8", "babel-loader": "^7.1.3", + "babel-plugin-react-intl": "^2.4.0", "babel-plugin-transform-class-properties": "^6.24.1", + "babel-plugin-transform-react-jsx-img-import": "^0.1.4", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "cross-env": "^5.1.3", @@ -64,7 +64,7 @@ "extract-text-webpack-plugin": "4.0.0-alpha.0", "file-loader": "^1.1.10", "html-loader": "^0.5.5", - "html-webpack-plugin": "webpack-contrib/html-webpack-plugin", + "html-webpack-plugin": "^3.1.0", "lodash": "^4.17.5", "node-sass": "^4.7.2", "prettier": "^1.7.0", diff --git a/scripts/build_local_reticulum.sh b/scripts/build_local_reticulum.sh new file mode 100755 index 0000000000000000000000000000000000000000..118a801e24fba4fdbed51c5b23f793db46cc498f --- /dev/null +++ b/scripts/build_local_reticulum.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +if [ ! -e ../reticulum ]; then + echo "This script assumes reticulum is checked out in a sibling to this folder." +fi + +rm -rf ../reticulum/priv/static ; BASE_ASSETS_PATH=http://localhost:4000/ yarn build -- --output-path ../reticulum/priv/static diff --git a/scripts/default.env b/scripts/default.env index 6a39a2127153c467bb2dac5e90b6cf485101d0e0..0fd18aa5be8791b61c69d2587c4611ef7f1f6b70 100644 --- a/scripts/default.env +++ b/scripts/default.env @@ -1,5 +1,5 @@ # This origin trial token is used to enable WebVR and Gamepad Extensions on Chrome 62+ # You can find more information about getting your own origin trial token here: https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/developer-guide.md ORIGIN_TRIAL_TOKEN="AvIMoF4hyRZQVfSfksoqP+7qzwa4FSBzHRHvUyzC8rMATJVRbcOiLewBxbXtJVyV3N62gsZv7PoSNtDqqtjzYAcAAABkeyJvcmlnaW4iOiJodHRwczovL3JldGljdWx1bS5pbzo0NDMiLCJmZWF0dXJlIjoiV2ViVlIxLjFNNjIiLCJleHBpcnkiOjE1MTYxNDYyMDQsImlzU3ViZG9tYWluIjp0cnVlfQ==", -ORIGIN_TRIAL_EXPIRES="2018-01-16", -JANUS_SERVER="wss://dev-janus.reticulum.io" \ No newline at end of file +ORIGIN_TRIAL_EXPIRES="2018-05-15", +JANUS_SERVER="wss://prod-janus.reticulum.io" diff --git a/src/activators/pressedmove.js b/src/activators/pressedmove.js index c035383186e0997908be2eae5868a1a97e546dd4..fb7d7470723246b687049eafeb83d51e84f0e8bd 100644 --- a/src/activators/pressedmove.js +++ b/src/activators/pressedmove.js @@ -18,10 +18,10 @@ PressedMove.prototype = { this.onActivate(event); } }, - onButtonDown: function(event) { + onButtonDown: function() { this.pressed = true; }, - onButtonUp: function(event) { + onButtonUp: function() { this.pressed = false; }, diff --git a/src/activators/shortpress.js b/src/activators/shortpress.js index 7f47450bef78161554a89569d4a610d7f7691877..fd9b8da0a212dc6acfba55509072d9c46c079305 100644 --- a/src/activators/shortpress.js +++ b/src/activators/shortpress.js @@ -16,13 +16,12 @@ function ShortPress(el, button, onActivate) { ShortPress.prototype = { onButtonDown(event) { - var self = this; - this.pressTimer = window.setTimeout(function() { - self.onActivate(event); + this.pressTimer = window.setTimeout(() => { + this.onActivate(event); }, this.timeOut); }, - onButtonUp(event) { + onButtonUp() { clearTimeout(this.pressTimer); }, diff --git a/src/assets/avatars/BotBobo_Avatar.glb b/src/assets/avatars/BotBobo_Avatar.glb deleted file mode 100644 index 66862adb28f35fb3bb1ca4954d840a1f9a91bb96..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/BotBobo_Avatar.glb and /dev/null differ diff --git a/src/assets/avatars/BotBobo_Avatar_Unlit.glb b/src/assets/avatars/BotBobo_Avatar_Unlit.glb deleted file mode 100644 index e4a939a242fc930dc52c7d60aa145b7b86023330..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/BotBobo_Avatar_Unlit.glb and /dev/null differ diff --git a/src/assets/avatars/BotDefault_Avatar.glb b/src/assets/avatars/BotDefault_Avatar.glb deleted file mode 100644 index 0784e7354e0fe2e35b3df8329c5587616203b7c3..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/BotDefault_Avatar.glb and /dev/null differ diff --git a/src/assets/avatars/BotDefault_Avatar_Unlit.glb b/src/assets/avatars/BotDefault_Avatar_Unlit.glb deleted file mode 100644 index 850f7552c4183553779dfe0776fb92f042b7f8ca..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/BotDefault_Avatar_Unlit.glb and /dev/null differ diff --git a/src/assets/avatars/BotDom_Avatar.glb b/src/assets/avatars/BotDom_Avatar.glb deleted file mode 100644 index 098a042ead7b919170c6c4c6a63ba40f62a1b81b..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/BotDom_Avatar.glb and /dev/null differ diff --git a/src/assets/avatars/BotDom_Avatar_Unlit.glb b/src/assets/avatars/BotDom_Avatar_Unlit.glb deleted file mode 100644 index 88c830ae9f73581683050705ad75f321bdeecb37..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/BotDom_Avatar_Unlit.glb and /dev/null differ diff --git a/src/assets/avatars/BotGreg_Avatar.glb b/src/assets/avatars/BotGreg_Avatar.glb deleted file mode 100644 index 33ca3ee66249ed267688353ef0ab82fd97dfc8bc..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/BotGreg_Avatar.glb and /dev/null differ diff --git a/src/assets/avatars/BotGreg_Avatar_Unlit.glb b/src/assets/avatars/BotGreg_Avatar_Unlit.glb deleted file mode 100644 index ed0a5291790f79029b6c7e624a45253c5f6caeb3..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/BotGreg_Avatar_Unlit.glb and /dev/null differ diff --git a/src/assets/avatars/BotGuest_Avatar.glb b/src/assets/avatars/BotGuest_Avatar.glb deleted file mode 100644 index e0be8e9cbad1a1d6167c0c0ccf333285d0857472..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/BotGuest_Avatar.glb and /dev/null differ diff --git a/src/assets/avatars/BotGuest_Avatar_Unlit.glb b/src/assets/avatars/BotGuest_Avatar_Unlit.glb deleted file mode 100644 index 3bc50b18e3ffbf9f9007a6f7790a8512284949a7..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/BotGuest_Avatar_Unlit.glb and /dev/null differ diff --git a/src/assets/avatars/BotJim_Avatar.glb b/src/assets/avatars/BotJim_Avatar.glb deleted file mode 100644 index 34c1a8cf274d0ed62b2bc22b51d573f6bafc1728..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/BotJim_Avatar.glb and /dev/null differ diff --git a/src/assets/avatars/BotJim_Avatar_Unlit.glb b/src/assets/avatars/BotJim_Avatar_Unlit.glb deleted file mode 100644 index 534356f80deb17b8808bc778db2db048811baf6a..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/BotJim_Avatar_Unlit.glb and /dev/null differ diff --git a/src/assets/avatars/BotKev_Avatar.glb b/src/assets/avatars/BotKev_Avatar.glb deleted file mode 100644 index a20a54bcc42926a79b623e360a4ed9ddb2380695..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/BotKev_Avatar.glb and /dev/null differ diff --git a/src/assets/avatars/BotKev_Avatar_Unlit.glb b/src/assets/avatars/BotKev_Avatar_Unlit.glb deleted file mode 100644 index d70196b7a3f83f25445cef27cf0fbc7493805cd2..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/BotKev_Avatar_Unlit.glb and /dev/null differ diff --git a/src/assets/avatars/BotPinky_Avatar.glb b/src/assets/avatars/BotPinky_Avatar.glb deleted file mode 100644 index da14ccd6ada339e7d28c1dfcd636a0810d45907c..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/BotPinky_Avatar.glb and /dev/null differ diff --git a/src/assets/avatars/BotPinky_Avatar_Unlit.glb b/src/assets/avatars/BotPinky_Avatar_Unlit.glb deleted file mode 100644 index 4029c8c6feb3dfe137d932ff0e7eb9223c60b6d1..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/BotPinky_Avatar_Unlit.glb and /dev/null differ diff --git a/src/assets/avatars/BotRobert_Avatar.glb b/src/assets/avatars/BotRobert_Avatar.glb deleted file mode 100644 index 68d28e5a19616eae6e5e5c8694182efe02643c65..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/BotRobert_Avatar.glb and /dev/null differ diff --git a/src/assets/avatars/BotRobert_Avatar_Unlit.glb b/src/assets/avatars/BotRobert_Avatar_Unlit.glb deleted file mode 100644 index 505eae50b5604bdbe3a7076421dabd1847e561f1..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/BotRobert_Avatar_Unlit.glb and /dev/null differ diff --git a/src/assets/avatars/BotWoody_Avatar.glb b/src/assets/avatars/BotWoody_Avatar.glb deleted file mode 100644 index 0a205ee9311b5c73f96f5fdf562970bd362b1a21..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/BotWoody_Avatar.glb and /dev/null differ diff --git a/src/assets/avatars/BotWoody_Avatar_Unlit.glb b/src/assets/avatars/BotWoody_Avatar_Unlit.glb deleted file mode 100644 index bd330d47f269ccaae436fbb3a33aa85b51eee6fd..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/BotWoody_Avatar_Unlit.glb and /dev/null differ diff --git a/src/assets/avatars/avatars.js b/src/assets/avatars/avatars.js index f7a5500048710c5bf0910e5bc781000764871312..68d26045503229c4a8bd9d8952b0e032b49b0b29 100644 --- a/src/assets/avatars/avatars.js +++ b/src/assets/avatars/avatars.js @@ -1,65 +1,42 @@ export const avatars = [ { - "id": "botdefault", - "models": { - "low": `${ require("./BotDefault_Avatar_Unlit.glb") }`, - "high": `${ require("./BotDefault_Avatar.glb") }` - } + id: "botdefault", + model: "https://asset-bundles-dev.reticulum.io/bots/BotDefault_Avatar-9f71f8ff22.gltf" }, { - "id": "botbobo", - "models": { - "low": `${ require("./BotBobo_Avatar_Unlit.glb") }`, - "high": `${ require("./BotBobo_Avatar.glb") }` - } + id: "botbobo", + model: "https://asset-bundles-dev.reticulum.io/bots/BotBobo_Avatar-f9740a010b.gltf" }, { - "id": "botdom", - "models": { - "low": `${ require("./BotDom_Avatar_Unlit.glb") }`, - "high": `${ require("./BotDom_Avatar.glb") }` - } + id: "botdom", + model: "https://asset-bundles-dev.reticulum.io/bots/BotDom_Avatar-6aa1b5d781.gltf" }, { - "id": "botgreg", - "models": { - "low": `${ require("./BotGreg_Avatar_Unlit.glb") }`, - "high": `${ require("./BotGreg_Avatar.glb") }` - } + id: "botgreg", + model: "https://asset-bundles-dev.reticulum.io/bots/BotGreg_Avatar-98d39797bb.gltf" }, { - "id": "botguest", - "models": { - "low": `${ require("./BotGuest_Avatar_Unlit.glb") }`, - "high": `${ require("./BotGuest_Avatar.glb") }` - } + id: "botguest", + model: "https://asset-bundles-dev.reticulum.io/bots/BotGuest_Avatar-78cd857332.gltf" }, { - "id": "botjim", - "models": { - "low": `${ require("./BotJim_Avatar_Unlit.glb") }`, - "high": `${ require("./BotJim_Avatar.glb") }` - } + id: "botjim", + model: "https://asset-bundles-dev.reticulum.io/bots/BotJim_Avatar-d28005a687.gltf" }, { - "id": "botpinky", - "models": { - "low": `${ require("./BotPinky_Avatar_Unlit.glb") }`, - "high": `${ require("./BotPinky_Avatar.glb") }` - } + id: "botkev", + model: "https://asset-bundles-dev.reticulum.io/bots/BotKev_Avatar-a95787bb51.gltf" }, { - "id": "botrobert", - "models": { - "low": `${ require("./BotRobert_Avatar_Unlit.glb") }`, - "high": `${ require("./BotRobert_Avatar.glb") }` - } + id: "botpinky", + model: "https://asset-bundles-dev.reticulum.io/bots/BotPinky_Avatar-b0b93f8675.gltf" }, { - "id": "botwoody", - "models": { - "low": `${ require("./BotWoody_Avatar_Unlit.glb") }`, - "high": `${ require("./BotWoody_Avatar.glb") }` - } + id: "botrobert", + model: "https://asset-bundles-dev.reticulum.io/bots/BotRobert_Avatar-e9554880f3.gltf" + }, + { + id: "botwoody", + model: "https://asset-bundles-dev.reticulum.io/bots/BotWoody_Avatar-0140485a23.gltf" } ]; diff --git a/src/assets/avatars/dodec/AvatarHand_L/AvatarDodeca_Texture.png b/src/assets/avatars/dodec/AvatarHand_L/AvatarDodeca_Texture.png deleted file mode 100644 index d0f89bc1497e8bc97c7c035abde9705e1316b613..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/dodec/AvatarHand_L/AvatarDodeca_Texture.png and /dev/null differ diff --git a/src/assets/avatars/dodec/AvatarHand_L/Avatar_HandL.bin b/src/assets/avatars/dodec/AvatarHand_L/Avatar_HandL.bin deleted file mode 100644 index ec35a43eff862c13c920dc70127e296629a2075f..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/dodec/AvatarHand_L/Avatar_HandL.bin and /dev/null differ diff --git a/src/assets/avatars/dodec/AvatarHand_L/Avatar_HandL.gltf b/src/assets/avatars/dodec/AvatarHand_L/Avatar_HandL.gltf deleted file mode 100644 index b47180f7e925c0866321a9387ba96bc161101f5e..0000000000000000000000000000000000000000 --- a/src/assets/avatars/dodec/AvatarHand_L/Avatar_HandL.gltf +++ /dev/null @@ -1 +0,0 @@ -{"accessors":[{"bufferView":0,"componentType":5126,"count":540,"type":"VEC3","max":[0.21502137184143066,0.31034931540489197,0.30054089426994324],"min":[-0.21345257759094238,-0.28918552398681641,-0.6024399995803833]},{"bufferView":1,"componentType":5126,"count":540,"type":"VEC3","max":[0.99698549509048462,0.97377252578735352,0.63385242223739624],"min":[-0.99751198291778564,-0.9912988543510437,-0.99289071559906006]},{"bufferView":2,"componentType":5126,"count":540,"type":"VEC4","max":[0.999259889125824,0.98216503858566284,0.98798274993896484,-1.0],"min":[-0.99856418371200562,-0.99964511394500732,-0.99477094411849976,-1.0]},{"bufferView":3,"componentType":5126,"count":540,"type":"VEC2","max":[0.49392947554588318,-0.12775179743766785],"min":[0.00923137180507183,-0.49687236547470093]},{"bufferView":4,"componentType":5123,"count":2328,"type":"SCALAR","max":[539.0],"min":[0.0]}],"asset":{"version":"2.0"},"buffers":[{"uri":"Avatar_HandL.bin","byteLength":30576}],"bufferViews":[{"buffer":0,"byteLength":6480},{"buffer":0,"byteOffset":6480,"byteLength":6480},{"buffer":0,"byteOffset":12960,"byteLength":8640},{"buffer":0,"byteOffset":21600,"byteLength":4320},{"buffer":0,"byteOffset":25920,"byteLength":4656}],"images":[{"uri":"AvatarDodeca_Texture.png","name":"AvatarDodeca_Texture"}],"materials":[{"pbrMetallicRoughness":{"baseColorTexture":{"index":0},"metallicFactor":0.0},"name":"AvatarDodeca_Texture"}],"meshes":[{"primitives":[{"attributes":{"POSITION":0,"NORMAL":1,"TANGENT":2,"TEXCOORD_0":3},"indices":4,"material":0}],"name":"Avatar_HandL"}],"nodes":[{"mesh":0,"name":"Avatar_HandL"}],"samplers":[{}],"scene":0,"scenes":[{"nodes":[0],"name":"Avatar_HandL"}],"textures":[{"sampler":0,"source":0,"name":"AvatarDodeca_Texture"}]} \ No newline at end of file diff --git a/src/assets/avatars/dodec/AvatarHand_R/AvatarDodeca_Texture.png b/src/assets/avatars/dodec/AvatarHand_R/AvatarDodeca_Texture.png deleted file mode 100644 index d0f89bc1497e8bc97c7c035abde9705e1316b613..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/dodec/AvatarHand_R/AvatarDodeca_Texture.png and /dev/null differ diff --git a/src/assets/avatars/dodec/AvatarHand_R/Avatar_HandR.bin b/src/assets/avatars/dodec/AvatarHand_R/Avatar_HandR.bin deleted file mode 100644 index 4861e7a7c94b1bb610660e8e1f1da95c0e8fd563..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/dodec/AvatarHand_R/Avatar_HandR.bin and /dev/null differ diff --git a/src/assets/avatars/dodec/AvatarHand_R/Avatar_HandR.gltf b/src/assets/avatars/dodec/AvatarHand_R/Avatar_HandR.gltf deleted file mode 100644 index d7b3042b39aafbdd63400397de57b0f3c4c5a8d8..0000000000000000000000000000000000000000 --- a/src/assets/avatars/dodec/AvatarHand_R/Avatar_HandR.gltf +++ /dev/null @@ -1 +0,0 @@ -{"accessors":[{"bufferView":0,"componentType":5126,"count":540,"type":"VEC3","max":[0.21345257759094238,0.31034931540489197,0.30054089426994324],"min":[-0.21502137184143066,-0.28918552398681641,-0.6024399995803833]},{"bufferView":1,"componentType":5126,"count":540,"type":"VEC3","max":[0.99751198291778564,0.97377252578735352,0.63385242223739624],"min":[-0.99698549509048462,-0.9912988543510437,-0.99289071559906006]},{"bufferView":2,"componentType":5126,"count":540,"type":"VEC4","max":[0.99856418371200562,0.98216503858566284,0.98798274993896484,1.0],"min":[-0.999259889125824,-0.99964511394500732,-0.99477094411849976,1.0]},{"bufferView":3,"componentType":5126,"count":540,"type":"VEC2","max":[0.99392950534820557,-0.12775179743766785],"min":[0.50923138856887817,-0.49687236547470093]},{"bufferView":4,"componentType":5123,"count":2328,"type":"SCALAR","max":[539.0],"min":[0.0]}],"asset":{"version":"2.0"},"buffers":[{"uri":"Avatar_HandR.bin","byteLength":30576}],"bufferViews":[{"buffer":0,"byteLength":6480},{"buffer":0,"byteOffset":6480,"byteLength":6480},{"buffer":0,"byteOffset":12960,"byteLength":8640},{"buffer":0,"byteOffset":21600,"byteLength":4320},{"buffer":0,"byteOffset":25920,"byteLength":4656}],"images":[{"uri":"AvatarDodeca_Texture.png","name":"AvatarDodeca_Texture"}],"materials":[{"pbrMetallicRoughness":{"baseColorTexture":{"index":0},"metallicFactor":0.0},"name":"AvatarDodeca_Texture"}],"meshes":[{"primitives":[{"attributes":{"POSITION":0,"NORMAL":1,"TANGENT":2,"TEXCOORD_0":3},"indices":4,"material":0}],"name":"Avatar_HandR"}],"nodes":[{"mesh":0,"name":"Avatar_HandR"}],"samplers":[{}],"scene":0,"scenes":[{"nodes":[0],"name":"Avatar_HandR"}],"textures":[{"sampler":0,"source":0,"name":"AvatarDodeca_Texture"}]} \ No newline at end of file diff --git a/src/assets/avatars/dodec/DodecAvatarGLTF/AvatarDodeca_Texture.png b/src/assets/avatars/dodec/DodecAvatarGLTF/AvatarDodeca_Texture.png deleted file mode 100644 index 3f7585fbd2ec32b717296daedb765cad19b9daa7..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/dodec/DodecAvatarGLTF/AvatarDodeca_Texture.png and /dev/null differ diff --git a/src/assets/avatars/dodec/DodecAvatarGLTF/DodecAvatar_Head.bin b/src/assets/avatars/dodec/DodecAvatarGLTF/DodecAvatar_Head.bin deleted file mode 100644 index b4284d677642861c1d1520e36ea43cffc1e6516f..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/dodec/DodecAvatarGLTF/DodecAvatar_Head.bin and /dev/null differ diff --git a/src/assets/avatars/dodec/DodecAvatarGLTF/DodecAvatar_Head.gltf b/src/assets/avatars/dodec/DodecAvatarGLTF/DodecAvatar_Head.gltf deleted file mode 100644 index 94949cd5b070a5bf5f87703339f62076b020be3e..0000000000000000000000000000000000000000 --- a/src/assets/avatars/dodec/DodecAvatarGLTF/DodecAvatar_Head.gltf +++ /dev/null @@ -1 +0,0 @@ -{"accessors":[{"bufferView":0,"componentType":5126,"count":384,"type":"VEC3","max":[0.47052082419395447,0.47052082419395447,0.47052082419395447],"min":[-0.47052082419395447,-0.47052082419395447,-0.47052082419395447]},{"bufferView":1,"componentType":5126,"count":384,"type":"VEC3","max":[0.99266362190246582,0.987006425857544,1.0],"min":[-0.99266362190246582,-0.98700630664825439,-1.0]},{"bufferView":2,"componentType":5126,"count":384,"type":"VEC4","max":[1.0,1.0,0.987902820110321,1.0],"min":[-1.0,-1.0,-0.95196151733398438,-1.0]},{"bufferView":3,"componentType":5126,"count":384,"type":"VEC2","max":[0.99672305583953857,-0.5052105188369751],"min":[0.038782119750976563,-0.99697029590606689]},{"bufferView":4,"componentType":5123,"count":1464,"type":"SCALAR","max":[383.0],"min":[0.0]}],"asset":{"version":"2.0"},"buffers":[{"uri":"DodecAvatar_Head.bin","byteLength":21360}],"bufferViews":[{"buffer":0,"byteLength":4608},{"buffer":0,"byteOffset":4608,"byteLength":4608},{"buffer":0,"byteOffset":9216,"byteLength":6144},{"buffer":0,"byteOffset":15360,"byteLength":3072},{"buffer":0,"byteOffset":18432,"byteLength":2928}],"images":[{"uri":"AvatarDodeca_Texture.png","name":"AvatarDodeca_Texture"}],"materials":[{"pbrMetallicRoughness":{"baseColorTexture":{"index":0},"metallicFactor":0.0},"name":"AvatarDodeca_Texture"}],"meshes":[{"primitives":[{"attributes":{"POSITION":0,"NORMAL":1,"TANGENT":2,"TEXCOORD_0":3},"indices":4,"material":0}],"name":"DodecAvatar_Head"}],"nodes":[{"mesh":0,"name":"DodecAvatar_Head"}],"samplers":[{}],"scene":0,"scenes":[{"nodes":[0],"name":"DodecAvatar_Head"}],"textures":[{"sampler":0,"source":0,"name":"AvatarDodeca_Texture"}]} \ No newline at end of file diff --git a/src/assets/environments/RockIslandTest/LowPolyGradients.png b/src/assets/environments/RockIslandTest/LowPolyGradients.png deleted file mode 100644 index 694117894c4e7e32ba67e64547f3bd5ba58d991f..0000000000000000000000000000000000000000 Binary files a/src/assets/environments/RockIslandTest/LowPolyGradients.png and /dev/null differ diff --git a/src/assets/environments/RockIslandTest/RockIsland.bin b/src/assets/environments/RockIslandTest/RockIsland.bin deleted file mode 100644 index f739cbb7fdc727e2c9f7f2b66364bd9ece6425c7..0000000000000000000000000000000000000000 Binary files a/src/assets/environments/RockIslandTest/RockIsland.bin and /dev/null differ diff --git a/src/assets/environments/RockIslandTest/RockIsland.gltf b/src/assets/environments/RockIslandTest/RockIsland.gltf deleted file mode 100644 index f98afc3480872c4a8fb44e991f5bd4694a8c3e96..0000000000000000000000000000000000000000 --- a/src/assets/environments/RockIslandTest/RockIsland.gltf +++ /dev/null @@ -1 +0,0 @@ -{"accessors":[{"bufferView":0,"componentType":5126,"count":1411,"type":"VEC3","max":[19.999996185302734,3.1086244689504383E-15,20.000003814697266],"min":[-20.000003814697266,-1.3123339414596558,-19.999996185302734]},{"bufferView":1,"componentType":5126,"count":1411,"type":"VEC3","max":[0.26660636067390442,1.0,0.33901205658912659],"min":[-0.20681542158126831,0.932644248008728,-0.23218832910060883]},{"bufferView":2,"componentType":5126,"count":1411,"type":"VEC4","max":[-0.96324127912521362,0.26863479614257813,0.00802996288985014,1.0],"min":[-1.0,-0.21762958168983459,-0.0092367539182305336,1.0]},{"bufferView":3,"componentType":5126,"count":1411,"type":"VEC2","max":[1.0,-0.5],"min":[0.5,-1.0]},{"bufferView":4,"componentType":5123,"count":7926,"type":"SCALAR","max":[1410.0],"min":[0.0]},{"bufferView":5,"componentType":5126,"count":648,"type":"VEC3","max":[2.9559423923492432,2.1683521270751953,3.1799838542938232],"min":[-3.5352246761322021,-0.44372734427452087,-3.1799838542938232]},{"bufferView":6,"componentType":5126,"count":648,"type":"VEC3","max":[0.999163031578064,0.99966990947723389,0.98909211158752441],"min":[-0.97800123691558838,-1.0,-0.99961471557617188]},{"bufferView":7,"componentType":5126,"count":648,"type":"VEC4","max":[1.0,0.879211962223053,1.0,1.0],"min":[-0.99995076656341553,-0.99060577154159546,-0.99919396638870239,-1.0]},{"bufferView":8,"componentType":5126,"count":648,"type":"VEC2","max":[0.43582868576049805,-0.0074357390403747559],"min":[0.0037140846252441406,-0.99294942617416382]},{"bufferView":9,"componentType":5123,"count":1086,"type":"SCALAR","max":[647.0],"min":[0.0]},{"bufferView":10,"componentType":5126,"count":648,"type":"VEC3","max":[2.9559423923492432,2.1683521270751953,3.1799838542938232],"min":[-3.5352246761322021,-0.44372734427452087,-3.1799838542938232]},{"bufferView":11,"componentType":5126,"count":648,"type":"VEC3","max":[0.999163031578064,0.99966990947723389,0.98909211158752441],"min":[-0.97800123691558838,-1.0,-0.99961471557617188]},{"bufferView":12,"componentType":5126,"count":648,"type":"VEC4","max":[1.0,0.879211962223053,1.0,1.0],"min":[-0.99995076656341553,-0.99060577154159546,-0.99919396638870239,-1.0]},{"bufferView":13,"componentType":5126,"count":648,"type":"VEC2","max":[0.43582868576049805,-0.0074357390403747559],"min":[0.0037140846252441406,-0.99294942617416382]},{"bufferView":14,"componentType":5123,"count":1086,"type":"SCALAR","max":[647.0],"min":[0.0]},{"bufferView":15,"componentType":5126,"count":546,"type":"VEC3","max":[2.9899981021881104,3.5676181316375732,2.8179035186767578],"min":[-3.8418266773223877,-0.040709495544433594,-3.4786128997802734]},{"bufferView":16,"componentType":5126,"count":546,"type":"VEC3","max":[0.96864801645278931,0.99882489442825317,0.99907445907592773],"min":[-0.98354208469390869,-0.74868243932724,-0.98847699165344238]},{"bufferView":17,"componentType":5126,"count":546,"type":"VEC4","max":[1.0,0.96006858348846436,0.99997138977050781,1.0],"min":[-0.99678885936737061,-0.99173903465271,-0.99109262228012085,1.0]},{"bufferView":18,"componentType":5126,"count":546,"type":"VEC2","max":[0.49659347534179688,-0.017128758132457733],"min":[0.25181496143341064,-0.997360348701477]},{"bufferView":19,"componentType":5123,"count":1014,"type":"SCALAR","max":[545.0],"min":[0.0]},{"bufferView":20,"componentType":5126,"count":546,"type":"VEC3","max":[2.9899981021881104,3.5676181316375732,2.8179035186767578],"min":[-3.8418266773223877,-0.040709495544433594,-3.4786128997802734]},{"bufferView":21,"componentType":5126,"count":546,"type":"VEC3","max":[0.96864801645278931,0.99882489442825317,0.99907445907592773],"min":[-0.98354208469390869,-0.74868243932724,-0.98847699165344238]},{"bufferView":22,"componentType":5126,"count":546,"type":"VEC4","max":[1.0,0.96006858348846436,0.99997138977050781,1.0],"min":[-0.99678885936737061,-0.99173903465271,-0.99109262228012085,1.0]},{"bufferView":23,"componentType":5126,"count":546,"type":"VEC2","max":[0.49721819162368774,-0.017128758132457733],"min":[0.25243967771530151,-0.997360348701477]},{"bufferView":24,"componentType":5123,"count":1014,"type":"SCALAR","max":[545.0],"min":[0.0]},{"bufferView":25,"componentType":5126,"count":546,"type":"VEC3","max":[2.9899981021881104,3.5676181316375732,2.8179035186767578],"min":[-3.8418266773223877,-0.040709495544433594,-3.4786128997802734]},{"bufferView":26,"componentType":5126,"count":546,"type":"VEC3","max":[0.96864801645278931,0.99882489442825317,0.99907445907592773],"min":[-0.98354208469390869,-0.74868243932724,-0.98847699165344238]},{"bufferView":27,"componentType":5126,"count":546,"type":"VEC4","max":[1.0,0.96006858348846436,0.99997138977050781,1.0],"min":[-0.99678885936737061,-0.99173903465271,-0.99109262228012085,1.0]},{"bufferView":28,"componentType":5126,"count":546,"type":"VEC2","max":[0.24725833535194397,-0.017128758132457733],"min":[0.0024798214435577393,-0.997360348701477]},{"bufferView":29,"componentType":5123,"count":1014,"type":"SCALAR","max":[545.0],"min":[0.0]},{"bufferView":30,"componentType":5126,"count":82,"type":"VEC3","max":[0.93866407871246338,1.0926105976104736,0.86003494262695313],"min":[-0.61562132835388184,0.0,-0.91247844696044922]},{"bufferView":31,"componentType":5126,"count":82,"type":"VEC3","max":[0.94896394014358521,0.99244141578674316,0.99672383069992065],"min":[-0.99999809265136719,0.0,-0.94705736637115479]},{"bufferView":32,"componentType":5126,"count":82,"type":"VEC4","max":[0.99672383069992065,0.26544073224067688,0.99382126331329346,1.0],"min":[-0.98112761974334717,-0.342925488948822,-0.99999809265136719,1.0]},{"bufferView":33,"componentType":5126,"count":82,"type":"VEC2","max":[0.46181493997573853,-0.0877450704574585],"min":[0.25926116108894348,-0.982083797454834]},{"bufferView":34,"componentType":5121,"count":144,"type":"SCALAR","max":[81.0],"min":[0.0]},{"bufferView":35,"componentType":5126,"count":82,"type":"VEC3","max":[2.3649082183837891,1.0926105976104736,1.9330106973648071],"min":[-2.1655979156494141,0.0,-2.0063893795013428]},{"bufferView":36,"componentType":5126,"count":82,"type":"VEC3","max":[0.98967838287353516,0.99887090921401978,0.92581111192703247],"min":[-0.98946976661682129,0.0,-0.99940323829650879]},{"bufferView":37,"componentType":5126,"count":82,"type":"VEC4","max":[0.975090742111206,0.36814635992050171,0.99176400899887085,1.0],"min":[-0.99951469898223877,-0.22390003502368927,-0.99675518274307251,1.0]},{"bufferView":38,"componentType":5126,"count":82,"type":"VEC2","max":[0.46181493997573853,-0.0877450704574585],"min":[0.25926116108894348,-0.982083797454834]},{"bufferView":39,"componentType":5121,"count":144,"type":"SCALAR","max":[81.0],"min":[0.0]},{"bufferView":40,"componentType":5126,"count":108,"type":"VEC3","max":[1.2821140289306641,1.4500410556793213,0.71328639984130859],"min":[-1.5092668533325195,0.0,-2.6325387954711914]},{"bufferView":41,"componentType":5126,"count":108,"type":"VEC3","max":[0.99886143207550049,0.99783533811569214,0.95127010345458984],"min":[-0.94242948293685913,-0.033464446663856506,-0.98947012424468994]},{"bufferView":42,"componentType":5126,"count":108,"type":"VEC4","max":[0.97160744667053223,0.94099634885787964,0.99991941452026367,1.0],"min":[-0.99569183588027954,-0.98788422346115112,-0.9930422306060791,1.0]},{"bufferView":43,"componentType":5126,"count":108,"type":"VEC2","max":[0.498996376991272,-0.16156533360481262],"min":[0.28047823905944824,-0.99637091159820557]},{"bufferView":44,"componentType":5121,"count":192,"type":"SCALAR","max":[107.0],"min":[0.0]},{"bufferView":45,"componentType":5126,"count":108,"type":"VEC3","max":[1.2821140289306641,1.4500410556793213,0.71328639984130859],"min":[-1.5092668533325195,0.0,-2.6325387954711914]},{"bufferView":46,"componentType":5126,"count":108,"type":"VEC3","max":[0.99886143207550049,0.99783533811569214,0.95127010345458984],"min":[-0.94242948293685913,-0.033464446663856506,-0.98947012424468994]},{"bufferView":47,"componentType":5126,"count":108,"type":"VEC4","max":[0.97160744667053223,0.94099634885787964,0.99991941452026367,1.0],"min":[-0.99569183588027954,-0.98788422346115112,-0.9930422306060791,1.0]},{"bufferView":48,"componentType":5126,"count":108,"type":"VEC2","max":[0.498996376991272,-0.16156533360481262],"min":[0.28047823905944824,-0.99637091159820557]},{"bufferView":49,"componentType":5121,"count":192,"type":"SCALAR","max":[107.0],"min":[0.0]},{"bufferView":50,"componentType":5126,"count":403,"type":"VEC3","max":[16.000001907348633,0.0,16.000003814697266],"min":[-16.000001907348633,-15.020404815673828,-16.000003814697266]},{"bufferView":51,"componentType":5126,"count":403,"type":"VEC3","max":[0.95499247312545776,0.74890261888504028,0.97037827968597412],"min":[-0.99407404661178589,-0.98670178651809692,-0.95499193668365479]},{"bufferView":52,"componentType":5126,"count":403,"type":"VEC4","max":[1.0,0.787509560585022,0.99889814853668213,1.0],"min":[-1.0,-0.95876884460449219,-1.0,1.0]},{"bufferView":53,"componentType":5126,"count":403,"type":"VEC2","max":[1.3963935375213623,-0.0097050070762634277],"min":[-0.39781582355499268,-1.0070931911468506]},{"bufferView":54,"componentType":5123,"count":702,"type":"SCALAR","max":[402.0],"min":[0.0]},{"bufferView":55,"componentType":5126,"count":378,"type":"VEC3","max":[123.10870361328125,-38.315250396728516,1326.8095703125],"min":[-1236.1318359375,-966.20843505859375,-1620.9500732421875]},{"bufferView":56,"componentType":5126,"count":378,"type":"VEC3","max":[0.999698281288147,1.0,0.84968304634094238],"min":[-0.0063941203989088535,-0.27718955278396606,-0.87069481611251831]},{"bufferView":57,"componentType":5126,"count":378,"type":"VEC4","max":[0.99994182586669922,0.98820680379867554,0.999813973903656,1.0],"min":[-0.85544717311859131,-0.99870669841766357,-0.99975448846817017,1.0]},{"bufferView":58,"componentType":5126,"count":378,"type":"VEC2","max":[0.24664026498794556,-0.0046227052807807922],"min":[0.0012694597244262695,-0.98859697580337524]},{"bufferView":59,"componentType":5123,"count":960,"type":"SCALAR","max":[377.0],"min":[0.0]},{"bufferView":60,"componentType":5126,"count":1717,"type":"VEC3","max":[19.446229934692383,12.104448318481445,19.578594207763672],"min":[-19.710948944091797,-12.104447364807129,-19.578592300415039]},{"bufferView":61,"componentType":5126,"count":1717,"type":"VEC3","max":[0.98982715606689453,1.0,0.9982561469078064],"min":[-0.99698609113693237,-1.0,-0.99995458126068115]},{"bufferView":62,"componentType":5126,"count":1717,"type":"VEC4","max":[1.0,0.87921351194381714,0.998589813709259,1.0],"min":[-0.99999558925628662,-0.990605890750885,-0.99973934888839722,-1.0]},{"bufferView":63,"componentType":5126,"count":1717,"type":"VEC2","max":[0.9969220757484436,-0.0074357390403747559],"min":[0.00886639952659607,-0.99692213535308838]},{"bufferView":64,"componentType":5123,"count":2928,"type":"SCALAR","max":[1716.0],"min":[0.0]}],"asset":{"version":"2.0"},"buffers":[{"uri":"RockIsland.bin","byteLength":382836}],"bufferViews":[{"buffer":0,"byteLength":16932},{"buffer":0,"byteOffset":16932,"byteLength":16932},{"buffer":0,"byteOffset":33864,"byteLength":22576},{"buffer":0,"byteOffset":56440,"byteLength":11288},{"buffer":0,"byteOffset":67728,"byteLength":15852},{"buffer":0,"byteOffset":83580,"byteLength":7776},{"buffer":0,"byteOffset":91356,"byteLength":7776},{"buffer":0,"byteOffset":99132,"byteLength":10368},{"buffer":0,"byteOffset":109500,"byteLength":5184},{"buffer":0,"byteOffset":114684,"byteLength":2172},{"buffer":0,"byteOffset":116856,"byteLength":7776},{"buffer":0,"byteOffset":124632,"byteLength":7776},{"buffer":0,"byteOffset":132408,"byteLength":10368},{"buffer":0,"byteOffset":142776,"byteLength":5184},{"buffer":0,"byteOffset":147960,"byteLength":2172},{"buffer":0,"byteOffset":150132,"byteLength":6552},{"buffer":0,"byteOffset":156684,"byteLength":6552},{"buffer":0,"byteOffset":163236,"byteLength":8736},{"buffer":0,"byteOffset":171972,"byteLength":4368},{"buffer":0,"byteOffset":176340,"byteLength":2028},{"buffer":0,"byteOffset":178368,"byteLength":6552},{"buffer":0,"byteOffset":184920,"byteLength":6552},{"buffer":0,"byteOffset":191472,"byteLength":8736},{"buffer":0,"byteOffset":200208,"byteLength":4368},{"buffer":0,"byteOffset":204576,"byteLength":2028},{"buffer":0,"byteOffset":206604,"byteLength":6552},{"buffer":0,"byteOffset":213156,"byteLength":6552},{"buffer":0,"byteOffset":219708,"byteLength":8736},{"buffer":0,"byteOffset":228444,"byteLength":4368},{"buffer":0,"byteOffset":232812,"byteLength":2028},{"buffer":0,"byteOffset":234840,"byteLength":984},{"buffer":0,"byteOffset":235824,"byteLength":984},{"buffer":0,"byteOffset":236808,"byteLength":1312},{"buffer":0,"byteOffset":238120,"byteLength":656},{"buffer":0,"byteOffset":238776,"byteLength":144},{"buffer":0,"byteOffset":238920,"byteLength":984},{"buffer":0,"byteOffset":239904,"byteLength":984},{"buffer":0,"byteOffset":240888,"byteLength":1312},{"buffer":0,"byteOffset":242200,"byteLength":656},{"buffer":0,"byteOffset":242856,"byteLength":144},{"buffer":0,"byteOffset":243000,"byteLength":1296},{"buffer":0,"byteOffset":244296,"byteLength":1296},{"buffer":0,"byteOffset":245592,"byteLength":1728},{"buffer":0,"byteOffset":247320,"byteLength":864},{"buffer":0,"byteOffset":248184,"byteLength":192},{"buffer":0,"byteOffset":248376,"byteLength":1296},{"buffer":0,"byteOffset":249672,"byteLength":1296},{"buffer":0,"byteOffset":250968,"byteLength":1728},{"buffer":0,"byteOffset":252696,"byteLength":864},{"buffer":0,"byteOffset":253560,"byteLength":192},{"buffer":0,"byteOffset":253752,"byteLength":4836},{"buffer":0,"byteOffset":258588,"byteLength":4836},{"buffer":0,"byteOffset":263424,"byteLength":6448},{"buffer":0,"byteOffset":269872,"byteLength":3224},{"buffer":0,"byteOffset":273096,"byteLength":1404},{"buffer":0,"byteOffset":274500,"byteLength":4536},{"buffer":0,"byteOffset":279036,"byteLength":4536},{"buffer":0,"byteOffset":283572,"byteLength":6048},{"buffer":0,"byteOffset":289620,"byteLength":3024},{"buffer":0,"byteOffset":292644,"byteLength":1920},{"buffer":0,"byteOffset":294564,"byteLength":20604},{"buffer":0,"byteOffset":315168,"byteLength":20604},{"buffer":0,"byteOffset":335772,"byteLength":27472},{"buffer":0,"byteOffset":363244,"byteLength":13736},{"buffer":0,"byteOffset":376980,"byteLength":5856}],"images":[{"uri":"LowPolyGradients.png","name":"LowPolyGradients"}],"materials":[{"pbrMetallicRoughness":{"baseColorTexture":{"index":0},"metallicFactor":0.0},"name":"LowPolyGradients"}],"meshes":[{"primitives":[{"attributes":{"POSITION":0,"NORMAL":1,"TANGENT":2,"TEXCOORD_0":3},"indices":4,"material":0}],"name":"RockIsland_Meshes"},{"primitives":[{"attributes":{"POSITION":5,"NORMAL":6,"TANGENT":7,"TEXCOORD_0":8},"indices":9,"material":0}],"name":"RockCollapse_00"},{"primitives":[{"attributes":{"POSITION":10,"NORMAL":11,"TANGENT":12,"TEXCOORD_0":13},"indices":14,"material":0}],"name":"RockCollapse_01"},{"primitives":[{"attributes":{"POSITION":15,"NORMAL":16,"TANGENT":17,"TEXCOORD_0":18},"indices":19,"material":0}],"name":"RockMed_00"},{"primitives":[{"attributes":{"POSITION":20,"NORMAL":21,"TANGENT":22,"TEXCOORD_0":23},"indices":24,"material":0}],"name":"RockMed_01"},{"primitives":[{"attributes":{"POSITION":25,"NORMAL":26,"TANGENT":27,"TEXCOORD_0":28},"indices":29,"material":0}],"name":"RockMed_02"},{"primitives":[{"attributes":{"POSITION":30,"NORMAL":31,"TANGENT":32,"TEXCOORD_0":33},"indices":34,"material":0}],"name":"RockSmall_00"},{"primitives":[{"attributes":{"POSITION":35,"NORMAL":36,"TANGENT":37,"TEXCOORD_0":38},"indices":39,"material":0}],"name":"RockSmall_01"},{"primitives":[{"attributes":{"POSITION":40,"NORMAL":41,"TANGENT":42,"TEXCOORD_0":43},"indices":44,"material":0}],"name":"RockSmallA_01"},{"primitives":[{"attributes":{"POSITION":45,"NORMAL":46,"TANGENT":47,"TEXCOORD_0":48},"indices":49,"material":0}],"name":"RockSmallA_02"},{"primitives":[{"attributes":{"POSITION":50,"NORMAL":51,"TANGENT":52,"TEXCOORD_0":53},"indices":54,"material":0}],"name":"RockUnderChunk"},{"primitives":[{"attributes":{"POSITION":55,"NORMAL":56,"TANGENT":57,"TEXCOORD_0":58},"indices":59,"material":0}],"name":"RockLandscape_background"},{"primitives":[{"attributes":{"POSITION":60,"NORMAL":61,"TANGENT":62,"TEXCOORD_0":63},"indices":64,"material":0}],"name":"RockIsland_Floater (1)"}],"nodes":[{"children":[1,2,3,4,5,6,7,8,9,10],"mesh":0,"name":"RockIsland_Meshes"},{"mesh":1,"scale":[1.4877162,1.4877162,1.4877162],"translation":[4.975782,-0.778213739,12.5263834],"name":"RockCollapse_00"},{"mesh":2,"rotation":[2.24125927E-17,0.8660254,2.24125927E-17,0.5],"scale":[2.08280277,2.08280277,2.08280277],"translation":[-12.5198393,-0.08149375,0.669323862],"name":"RockCollapse_01"},{"mesh":3,"scale":[0.419126272,0.419126272,0.419126272],"translation":[-2.89321947,-0.181781,8.143376],"name":"RockMed_00"},{"mesh":4,"rotation":[-8.00039162E-17,-0.923879564,-8.00039162E-17,0.382683456],"scale":[0.2933884,0.2933884,0.2933884],"translation":[5.78990173,-0.618600249,-2.91498733],"name":"RockMed_01"},{"mesh":5,"scale":[0.838252544,0.838252544,0.838252544],"translation":[9.599285,-0.566143334,-9.459988],"name":"RockMed_02"},{"mesh":6,"scale":[0.419126272,0.419126272,0.419126272],"translation":[-1.54470789,-0.181781,7.39793253],"name":"RockSmall_00"},{"mesh":7,"scale":[0.419126272,0.419126272,0.419126272],"translation":[0.2531415,-0.181781,-10.5903826],"name":"RockSmall_01"},{"mesh":8,"rotation":[-6.123234E-17,-1.0,-6.123234E-17,-5.05321539E-16],"scale":[0.419126272,0.419126272,0.419126272],"translation":[-3.29713774,-0.3228769,-5.73409271],"name":"RockSmallA_01"},{"mesh":9,"rotation":[-6.123234E-17,-1.0,-6.123234E-17,-5.05321539E-16],"scale":[1.54238474,0.7711924,1.54238474],"translation":[11.5860052,-0.1732521,2.40355229],"name":"RockSmallA_02"},{"mesh":10,"scale":[1.237053,1.237053,1.237053],"translation":[1.90734863E-06,-1.382638,2.861023E-06],"name":"RockUnderChunk"},{"mesh":11,"name":"RockLandscape_background"},{"mesh":12,"rotation":[0.0,0.8957491,0.0,0.44456014],"translation":[-106.7,47.2,-114.0],"name":"RockIsland_Floater (1)"},{"mesh":12,"rotation":[0.0,0.802093,0.0,-0.597199142],"translation":[195.6,129.0,336.0],"name":"RockIsland_Floater (4)"},{"mesh":12,"rotation":[0.0,0.8117935,0.0,0.5839446],"translation":[-26.0,10.4,129.0],"name":"RockIsland_Floater"},{"mesh":12,"rotation":[0.0,-0.394923657,0.0,0.918714],"translation":[-288.3,150.8,116.4],"name":"RockIsland_Floater (3)"},{"mesh":12,"rotation":[0.0,-0.4841211,0.0,0.875001],"translation":[102.9,29.8,-102.6],"name":"RockIsland_Floater (2)"}],"samplers":[{}],"scene":0,"scenes":[{"nodes":[0,11,12,13,14,15,16],"name":"RockIsland"}],"textures":[{"sampler":0,"source":0,"name":"LowPolyGradients"}]} \ No newline at end of file diff --git a/src/assets/environments/CliffVista_mesh.glb b/src/assets/environments/cliff_meeting_space/CliffVista_mesh.glb similarity index 100% rename from src/assets/environments/CliffVista_mesh.glb rename to src/assets/environments/cliff_meeting_space/CliffVista_mesh.glb diff --git a/src/assets/environments/FloorNav_mesh.glb b/src/assets/environments/cliff_meeting_space/FloorNav_mesh.glb similarity index 100% rename from src/assets/environments/FloorNav_mesh.glb rename to src/assets/environments/cliff_meeting_space/FloorNav_mesh.glb diff --git a/src/assets/environments/MeetingSpace1_mesh.glb b/src/assets/environments/cliff_meeting_space/MeetingSpace1_mesh.glb similarity index 100% rename from src/assets/environments/MeetingSpace1_mesh.glb rename to src/assets/environments/cliff_meeting_space/MeetingSpace1_mesh.glb diff --git a/src/assets/environments/OutdoorFacade_mesh.glb b/src/assets/environments/cliff_meeting_space/OutdoorFacade_mesh.glb similarity index 100% rename from src/assets/environments/OutdoorFacade_mesh.glb rename to src/assets/environments/cliff_meeting_space/OutdoorFacade_mesh.glb diff --git a/src/assets/environments/cliff_meeting_space/bundle.json.tpl b/src/assets/environments/cliff_meeting_space/bundle.json.tpl new file mode 100644 index 0000000000000000000000000000000000000000..6c54054858cec5ac982debae6351bfd6fa1664da --- /dev/null +++ b/src/assets/environments/cliff_meeting_space/bundle.json.tpl @@ -0,0 +1,25 @@ +{ + "name": "cliff_meeting_space", + "version": "0.1.0", + "meta": { + "title": "Cliffside Meeting Space", + "description": "Enjoy a beautiful vista view while collaborating in Mixed Reality", + "authors": [ + { "name": "Jim Conrad", "github": "j-conrad" } + ], + "images": [ + { "type": "preview-thumbnail", "srcset": "<%= require("./preview-thumbnail.png") %>" } + ] + }, + "assets": [ + { + "name": "space-geometry", "src": "<%= require("./MeetingSpace1_mesh.glb") %>" + }, + { + "name": "outdoor-geometry", "src": "<%= require("./OutdoorFacade_mesh.glb") %>" + }, + { + "name": "cliff-geometry", "src": "<%= require("./CliffVista_mesh.glb") %>" + } + ] +} diff --git a/src/assets/environments/cliff_meeting_space/preview-thumbnail.png b/src/assets/environments/cliff_meeting_space/preview-thumbnail.png new file mode 100755 index 0000000000000000000000000000000000000000..050f0e62619b45bb16fdd1bf648a0ba2696a6d46 Binary files /dev/null and b/src/assets/environments/cliff_meeting_space/preview-thumbnail.png differ diff --git a/src/assets/fonts/zilla-slab_latin-ext.woff2 b/src/assets/fonts/zilla-slab_latin-ext.woff2 deleted file mode 100644 index 60259781df0d9a85f8e9f88ef7ed3dcf8b69b12b..0000000000000000000000000000000000000000 Binary files a/src/assets/fonts/zilla-slab_latin-ext.woff2 and /dev/null differ diff --git a/src/assets/fonts/zilla-slab_latin.woff2 b/src/assets/fonts/zilla-slab_latin.woff2 deleted file mode 100644 index 18b5d6e8f5f3bd37ffe7effde8414b7ff3ed0409..0000000000000000000000000000000000000000 Binary files a/src/assets/fonts/zilla-slab_latin.woff2 and /dev/null differ diff --git a/src/assets/images/dice_icon.svg b/src/assets/images/dice_icon.svg new file mode 100755 index 0000000000000000000000000000000000000000..7625794485f657ff84d4323f113e34063f78905f --- /dev/null +++ b/src/assets/images/dice_icon.svg @@ -0,0 +1,22 @@ +<svg width="32" height="34" viewBox="0 0 32 34" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<title>dice_icon</title> +<desc>Created using Figma</desc> +<g id="Canvas" transform="translate(448 -293)"> +<g id="dice_icon"> +<g id="Group"> +<g id="Group"> +<g id="Vector"> +<use xlink:href="#path0_fill" transform="translate(-448 310.389)" fill="#FFFFFF"/> +</g> +<g id="Vector"> +<use xlink:href="#path1_fill" transform="translate(-436.032 293)" fill="#FFFFFF"/> +</g> +</g> +</g> +</g> +</g> +<defs> +<path id="path0_fill" d="M 12.7277 0L 3.47119 0C 1.55509 0 0 1.59463 0 3.55943L 0 13.0513C 0 15.0161 1.55509 16.6107 3.47119 16.6107L 12.7277 16.6107C 14.6449 16.6107 16.1989 15.0161 16.1989 13.0513L 16.1989 3.55943C 16.1989 1.59463 14.6449 0 12.7277 0ZM 4.04972 14.2377C 3.09167 14.2377 2.31412 13.4404 2.31412 12.458C 2.31412 11.4756 3.09167 10.6783 4.04972 10.6783C 5.00777 10.6783 5.78531 11.4756 5.78531 12.458C 5.78531 13.4404 5.00777 14.2377 4.04972 14.2377ZM 4.04972 5.93239C 3.09167 5.93239 2.31412 5.13508 2.31412 4.15267C 2.31412 3.17027 3.09167 2.37296 4.04972 2.37296C 5.00777 2.37296 5.78531 3.17027 5.78531 4.15267C 5.78531 5.13508 5.00777 5.93239 4.04972 5.93239ZM 8.09943 10.0851C 7.14139 10.0851 6.36384 9.28775 6.36384 8.30534C 6.36384 7.32294 7.14139 6.52563 8.09943 6.52563C 9.05748 6.52563 9.83503 7.32294 9.83503 8.30534C 9.83503 9.28775 9.05748 10.0851 8.09943 10.0851ZM 12.1492 14.2377C 11.1911 14.2377 10.4136 13.4404 10.4136 12.458C 10.4136 11.4756 11.1911 10.6783 12.1492 10.6783C 13.1072 10.6783 13.8847 11.4756 13.8847 12.458C 13.8847 13.4404 13.1072 14.2377 12.1492 14.2377ZM 12.1492 5.93239C 11.1911 5.93239 10.4136 5.13508 10.4136 4.15267C 10.4136 3.17027 11.1911 2.37296 12.1492 2.37296C 13.1072 2.37296 13.8847 3.17027 13.8847 4.15267C 13.8847 5.13508 13.1072 5.93239 12.1492 5.93239Z"/> +<path id="path1_fill" d="M 19.0155 7.75393L 12.47 1.04201C 11.115 -0.347359 8.91661 -0.347359 7.56169 1.04201L 1.01617 7.75393C -0.338747 9.14329 -0.338747 11.3976 1.01617 12.787L 7.56169 19.4989C 8.91661 20.8883 11.115 20.8883 12.47 19.4989L 19.0155 12.787C 20.3704 11.3976 20.3704 9.14329 19.0155 7.75393ZM 11.2423 17.4023C 10.5643 18.0964 9.46621 18.0964 8.78817 17.4023C 8.11013 16.7071 8.11013 15.5799 8.78817 14.8847C 9.46621 14.1906 10.5643 14.1906 11.2423 14.8847C 11.9203 15.5799 11.9203 16.7071 11.2423 17.4023ZM 11.2423 11.5281C 10.5643 12.2234 9.46621 12.2234 8.78817 11.5281C 8.11013 10.8328 8.11013 9.70687 8.78817 9.01159C 9.46621 8.31631 10.5643 8.31631 11.2423 9.01159C 11.9203 9.70687 11.9203 10.834 11.2423 11.5281ZM 11.2423 5.65623C 10.5643 6.35032 9.46621 6.35032 8.78817 5.65623C 8.11013 4.96095 8.11013 3.83382 8.78817 3.13855C 9.46621 2.44446 10.5643 2.44446 11.2423 3.13855C 11.9203 3.83382 11.9203 4.96095 11.2423 5.65623Z"/> +</defs> +</svg> diff --git a/src/assets/images/expand_dots_icon.svg b/src/assets/images/expand_dots_icon.svg new file mode 100755 index 0000000000000000000000000000000000000000..8d765ad21b0d4de41fa4c09771ebf83761cafd06 --- /dev/null +++ b/src/assets/images/expand_dots_icon.svg @@ -0,0 +1,20 @@ +<svg width="26" height="6" viewBox="0 0 26 6" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<title>expand_dots_icon</title> +<desc>Created using Figma</desc> +<g id="Canvas" transform="translate(455 -258)"> +<g id="expand_dots_icon"> +<g id="Ellipse"> +<use xlink:href="#path0_fill" transform="translate(-455 258)" fill="#FFFFFF"/> +</g> +<g id="Ellipse"> +<use xlink:href="#path0_fill" transform="translate(-435 258)" fill="#FFFFFF"/> +</g> +<g id="Ellipse"> +<use xlink:href="#path0_fill" transform="translate(-445 258)" fill="#FFFFFF"/> +</g> +</g> +</g> +<defs> +<path id="path0_fill" d="M 6 3C 6 4.65685 4.65685 6 3 6C 1.34315 6 0 4.65685 0 3C 0 1.34315 1.34315 0 3 0C 4.65685 0 6 1.34315 6 3Z"/> +</defs> +</svg> diff --git a/src/assets/images/hub_create_button_disabled.svg b/src/assets/images/hub_create_button_disabled.svg new file mode 100755 index 0000000000000000000000000000000000000000..168e43e80b55792126e69e85cd75ae5da2ec51e8 --- /dev/null +++ b/src/assets/images/hub_create_button_disabled.svg @@ -0,0 +1,20 @@ +<svg width="55" height="55" viewBox="0 0 55 55" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<title>hub_create_button_disabled</title> +<desc>Created using Figma</desc> +<g id="Canvas" transform="translate(-92 -236)"> +<g id="hub_create_button_disabled"> +<g id="Oval Copy"> +<use xlink:href="#path0_stroke" transform="translate(94 238)" fill="#C0C0C0"/> +</g> +<g id="Group 7"> +<g id="Union"> +<use xlink:href="#path1_fill" transform="translate(112.7 255.567)" fill="#C0C0C0"/> +</g> +</g> +</g> +</g> +<defs> +<path id="path0_stroke" d="M 25.5 52.5C 40.4117 52.5 52.5 40.4117 52.5 25.5L 49.5 25.5C 49.5 38.7548 38.7548 49.5 25.5 49.5L 25.5 52.5ZM 52.5 25.5C 52.5 10.5883 40.4117 -1.5 25.5 -1.5L 25.5 1.5C 38.7548 1.5 49.5 12.2452 49.5 25.5L 52.5 25.5ZM 25.5 -1.5C 10.5883 -1.5 -1.5 10.5883 -1.5 25.5L 1.5 25.5C 1.5 12.2452 12.2452 1.5 25.5 1.5L 25.5 -1.5ZM -1.5 25.5C -1.5 40.4117 10.5883 52.5 25.5 52.5L 25.5 49.5C 12.2452 49.5 1.5 38.7548 1.5 25.5L -1.5 25.5Z"/> +<path id="path1_fill" fill-rule="evenodd" d="M 6.8 1.52588e-05L 15.3 7.65001L 6.8 15.3L 6.80001 10.2L 7.62939e-06 10.2L 7.62939e-06 5.10002L 6.80001 5.10002L 6.8 1.52588e-05Z"/> +</defs> +</svg> diff --git a/src/assets/images/hub_create_button_enabled.svg b/src/assets/images/hub_create_button_enabled.svg new file mode 100755 index 0000000000000000000000000000000000000000..f696984717b06cf6448829a45f80fcdab17b1800 --- /dev/null +++ b/src/assets/images/hub_create_button_enabled.svg @@ -0,0 +1,20 @@ +<svg width="55" height="55" viewBox="0 0 55 55" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<title>hub_create_button_enabled</title> +<desc>Created using Figma</desc> +<g id="Canvas" transform="translate(-92 -236)"> +<g id="hub_create_button_enabled"> +<g id="Oval Copy"> +<use xlink:href="#path0_fill" transform="translate(94 238)" fill="#2F80ED"/> +<use xlink:href="#path1_stroke" transform="translate(94 238)" fill="#FFFFFF"/> +</g> +<g id="Union"> +<use xlink:href="#path2_fill" transform="translate(112.7 255.567)" fill="#FFFFFF"/> +</g> +</g> +</g> +<defs> +<path id="path0_fill" fill-rule="evenodd" d="M 25.5 51C 39.5833 51 51 39.5833 51 25.5C 51 11.4167 39.5833 0 25.5 0C 11.4167 0 0 11.4167 0 25.5C 0 39.5833 11.4167 51 25.5 51Z"/> +<path id="path1_stroke" d="M 25.5 52.5C 40.4117 52.5 52.5 40.4117 52.5 25.5L 49.5 25.5C 49.5 38.7548 38.7548 49.5 25.5 49.5L 25.5 52.5ZM 52.5 25.5C 52.5 10.5883 40.4117 -1.5 25.5 -1.5L 25.5 1.5C 38.7548 1.5 49.5 12.2452 49.5 25.5L 52.5 25.5ZM 25.5 -1.5C 10.5883 -1.5 -1.5 10.5883 -1.5 25.5L 1.5 25.5C 1.5 12.2452 12.2452 1.5 25.5 1.5L 25.5 -1.5ZM -1.5 25.5C -1.5 40.4117 10.5883 52.5 25.5 52.5L 25.5 49.5C 12.2452 49.5 1.5 38.7548 1.5 25.5L -1.5 25.5Z"/> +<path id="path2_fill" fill-rule="evenodd" d="M 6.8 0L 15.3 7.64999L 6.8 15.3L 6.8 10.2L 0 10.2L 0 5.10001L 6.8 5.10001L 6.8 0Z"/> +</defs> +</svg> diff --git a/src/assets/images/logo.svg b/src/assets/images/logo.svg new file mode 100755 index 0000000000000000000000000000000000000000..43d6250ba602598561cd23813f5b63b9898f2df7 --- /dev/null +++ b/src/assets/images/logo.svg @@ -0,0 +1,73 @@ +<svg width="1101" height="150" viewBox="0 0 1101 150" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<title>logo</title> +<desc>Created using Figma</desc> +<g id="Canvas" transform="matrix(3 0 0 3 1473 588)"> +<g id="logo"> +<g id="Rectangle 2.1"> +<use xlink:href="#path0_fill" transform="translate(-491 -196)" fill="#FFFFFF"/> +</g> +<g id="moz://a"> +<use xlink:href="#path1_fill" transform="translate(-481 -202)"/> +</g> +<g id="Group 5.1"> +<g id="duck"> +<use xlink:href="#path2_fill" transform="translate(-311 -199)" fill="#FFFFFF"/> +</g> +<g id="Group 4"> +<g id="Group 2"> +<g id="noun_12039_cc"> +<g id="Subtract"> +<use xlink:href="#path3_fill" transform="translate(-191.254 -196)" fill="url(#paint3_linear)"/> +</g> +</g> +<g id="Ellipse"> +<use xlink:href="#path4_fill" transform="matrix(0.998739 0.0501978 -0.0492734 0.998785 -178.487 -187.814)"/> +</g> +<g id="Mask Group"> +<mask id="mask0_alpha" mask-type="alpha"> +<g id="Vector"> +<use xlink:href="#path5_fill" transform="translate(-198.5 -190.014)" fill="#C4C4C4"/> +</g> +</mask> +<g id="Vector" mask="url(#mask0_alpha)"> +<use xlink:href="#path6_fill" transform="translate(-191.254 -196)" fill="#FF8A00"/> +</g> +</g> +<g id="Ellipse"> +<use xlink:href="#path7_fill" transform="translate(-175 -186)" fill="#FFFFFF"/> +</g> +</g> +<g id="Group 4.1"> +<g id="Group 3"> +<g id="Subtract"> +<use xlink:href="#path8_fill" transform="translate(-203 -157.13)" fill="url(#paint8_radial)"/> +</g> +</g> +</g> +</g> +</g> +</g> +</g> +<defs> +<linearGradient id="paint3_linear" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(3.54163e-15 56.5272 -57.8393 3.46129e-15 57.8393 1.67757e-22)"> +<stop offset="0" stop-color="#FFC000"/> +<stop offset="1" stop-color="#FFD600"/> +</linearGradient> +<radialGradient id="paint8_radial" cx="0.5" cy="0.5" r="0.5" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-5 -160 111.805 -3.49389 -13.4023 140.377)"> +<stop offset="0" stop-color="#2F80ED" stop-opacity="0"/> +<stop offset="0.154696" stop-color="#2F80ED"/> +<stop offset="0.508287" stop-color="#2F80ED"/> +<stop offset="0.883978" stop-color="#2F80ED"/> +<stop offset="0.983425" stop-color="#2F80ED" stop-opacity="0.51"/> +</radialGradient> +<path id="path0_fill" d="M 0 0L 172 0L 172 49L 0 49L 0 0Z"/> +<path id="path1_fill" d="M 41.472 45L 32.4 45L 32.4 32.808C 32.4 30.952 32.08 29.624 31.44 28.824C 30.832 28.024 29.92 27.624 28.704 27.624C 27.232 27.624 26.16 28.104 25.488 29.064C 24.848 30.024 24.528 31.256 24.528 32.76L 24.528 40.344L 27.408 40.344L 27.408 45L 18.336 45L 18.336 32.808C 18.336 30.952 18.016 29.624 17.376 28.824C 16.768 28.024 15.856 27.624 14.64 27.624C 13.136 27.624 12.064 28.104 11.424 29.064C 10.784 30.024 10.464 31.256 10.464 32.76L 10.464 40.344L 14.592 40.344L 14.592 45L 1.392 45L 1.392 40.344L 4.272 40.344L 4.272 28.056L 1.44 28.056L 1.44 23.352L 10.464 23.352L 10.464 26.616C 11.84 24.152 14.032 22.92 17.04 22.92C 18.608 22.92 20.048 23.304 21.36 24.072C 22.672 24.84 23.584 26.008 24.096 27.576C 25.376 24.472 27.728 22.92 31.152 22.92C 33.104 22.92 34.832 23.528 36.336 24.744C 37.84 25.96 38.592 27.848 38.592 30.408L 38.592 40.344L 41.472 40.344L 41.472 45ZM 66.1909 34.2C 66.1909 37.624 65.0709 40.36 62.8309 42.408C 60.5909 44.456 57.7429 45.48 54.2869 45.48C 50.9589 45.48 48.2389 44.52 46.1269 42.6C 44.0469 40.648 43.0069 37.928 43.0069 34.44C 43.0069 31.272 43.9669 28.552 45.8869 26.28C 47.8069 24.008 50.7189 22.872 54.6229 22.872C 58.5269 22.872 61.4229 24.008 63.3109 26.28C 65.2309 28.52 66.1909 31.16 66.1909 34.2ZM 59.8069 34.008C 59.8069 31.8 59.2949 30.184 58.2709 29.16C 57.2469 28.136 55.9989 27.624 54.5269 27.624C 52.9269 27.624 51.6629 28.2 50.7349 29.352C 49.8389 30.472 49.3909 32.056 49.3909 34.104C 49.3909 35.96 49.8069 37.528 50.6389 38.808C 51.5029 40.088 52.7829 40.728 54.4789 40.728C 56.0789 40.728 57.3589 40.136 58.3189 38.952C 59.3109 37.736 59.8069 36.088 59.8069 34.008ZM 87.8119 45L 68.8999 45L 68.2759 41.736L 79.8919 28.008L 73.4119 28.008L 72.4519 31.32L 67.9879 30.936L 68.7559 23.352L 87.7639 23.352L 88.2439 26.616L 76.2919 40.344L 83.2519 40.344L 84.2599 36.936L 89.1559 37.416L 87.8119 45ZM 92.8789 31.128L 92.8789 23.352L 99.3109 23.352L 99.3109 31.128L 92.8789 31.128ZM 92.8789 45L 92.8789 37.224L 99.3109 37.224L 99.3109 45L 92.8789 45ZM 119.281 11.976L 109.921 45L 103.825 45L 113.185 11.976L 119.281 11.976ZM 131.562 11.976L 122.202 45L 116.106 45L 125.466 11.976L 131.562 11.976ZM 133.858 24.888C 135.746 24.12 137.314 23.608 138.562 23.352C 139.842 23.064 141.362 22.92 143.122 22.92C 145.458 22.92 147.474 23.528 149.17 24.744C 150.866 25.96 151.714 27.8 151.714 30.264L 151.714 39.672C 151.714 40.792 152.21 41.352 153.202 41.352C 153.49 41.352 153.794 41.304 154.114 41.208L 154.162 44.472C 152.914 45.144 151.618 45.48 150.274 45.48C 147.426 45.48 145.842 44.056 145.522 41.208L 145.522 41.16C 144.914 42.248 144.034 43.24 142.882 44.136C 141.762 45.032 140.322 45.48 138.562 45.48C 136.994 45.48 135.49 45.016 134.05 44.088C 132.61 43.16 131.89 41.56 131.89 39.288C 131.89 36.632 132.946 34.872 135.058 34.008C 137.202 33.144 139.506 32.712 141.97 32.712C 143.346 32.712 144.53 32.776 145.522 32.904L 145.522 32.184C 145.522 31.064 145.362 29.976 145.042 28.92C 144.754 27.864 143.73 27.336 141.97 27.336C 141.33 27.336 140.738 27.384 140.194 27.48C 139.682 27.544 139.17 27.704 138.658 27.96L 137.842 31.032L 132.898 30.504L 133.858 24.888ZM 145.522 36.168L 145.522 35.88C 145.01 35.816 144.482 35.752 143.938 35.688C 143.426 35.624 142.914 35.592 142.402 35.592C 141.314 35.592 140.354 35.8 139.522 36.216C 138.69 36.6 138.274 37.384 138.274 38.568C 138.274 39.272 138.466 39.864 138.85 40.344C 139.234 40.824 139.89 41.064 140.818 41.064C 141.906 41.064 142.914 40.68 143.842 39.912C 144.802 39.112 145.362 37.864 145.522 36.168Z"/> +<path id="path2_fill" d="M 23.32 42L 17.292 42L 17.292 37.952C 16.6467 39.1253 15.752 40.1667 14.608 41.076C 13.4933 41.9853 12.0413 42.44 10.252 42.44C 7.75867 42.44 5.73467 41.5893 4.18 39.888C 2.62533 38.1867 1.848 35.8253 1.848 32.804C 1.848 31.1027 2.12667 29.4307 2.684 27.788C 3.27067 26.1453 4.20933 24.7813 5.5 23.696C 6.79067 22.6107 8.52133 22.068 10.692 22.068C 12.0707 22.068 13.3467 22.376 14.52 22.992C 15.6933 23.608 16.6173 24.5027 17.292 25.676L 17.292 13.752L 12.364 13.752L 12.364 11.728L 19.712 11.728L 19.712 39.976L 23.32 39.976L 23.32 42ZM 17.292 33.684L 17.292 29.856C 17.2333 28.2427 16.588 26.908 15.356 25.852C 14.1533 24.7667 12.7307 24.224 11.088 24.224C 8.68267 24.224 6.96667 25.104 5.94 26.864C 4.91333 28.624 4.4 30.5453 4.4 32.628C 4.4 35.1213 5.00133 37.028 6.204 38.348C 7.40667 39.6387 8.91733 40.284 10.736 40.284C 12.5547 40.284 14.08 39.5507 15.312 38.084C 16.5733 36.6173 17.2333 35.1507 17.292 33.684ZM 47.6496 42L 41.5776 42L 41.5776 38.128C 40.9616 39.4187 40.0523 40.46 38.8496 41.252C 37.6469 42.044 36.2536 42.44 34.6696 42.44C 32.7336 42.44 31.1203 41.8387 29.8296 40.636C 28.5389 39.4333 27.8936 37.4973 27.8936 34.828L 27.8936 24.532L 24.2856 24.532L 24.2856 22.464L 30.3136 22.464L 30.3136 34.256C 30.3136 36.4853 30.7829 38.0547 31.7216 38.964C 32.6896 39.844 33.9069 40.284 35.3736 40.284C 37.1336 40.284 38.5856 39.5947 39.7296 38.216C 40.9029 36.8373 41.5189 35.444 41.5776 34.036L 41.5776 24.532L 37.2656 24.532L 37.2656 22.464L 43.9976 22.464L 43.9976 39.976L 47.6496 39.976L 47.6496 42ZM 66.8638 35.62C 66.5705 37.3213 65.7932 38.8907 64.5318 40.328C 63.2998 41.736 61.4078 42.44 58.8558 42.44C 56.1865 42.44 54.0745 41.56 52.5198 39.8C 50.9652 38.04 50.1878 35.62 50.1878 32.54C 50.1878 30.868 50.4958 29.2253 51.1118 27.612C 51.7278 25.9987 52.6812 24.6787 53.9718 23.652C 55.2918 22.596 56.9785 22.068 59.0318 22.068C 61.7012 22.068 63.8865 22.772 65.5878 24.18L 66.2918 28.536L 64.2678 28.8L 63.5198 25.5C 62.3172 24.6493 60.8212 24.224 59.0318 24.224C 56.7145 24.224 55.0865 25.06 54.1478 26.732C 53.2385 28.3747 52.7838 30.252 52.7838 32.364C 52.7838 34.8573 53.3265 36.808 54.4118 38.216C 55.4972 39.5947 57.0665 40.284 59.1198 40.284C 62.2292 40.284 64.1212 38.568 64.7958 35.136L 66.8638 35.62ZM 90.5792 42L 81.5592 42L 81.5592 39.976L 84.8152 39.976L 78.9632 32.584L 75.3112 32.584L 75.3112 39.976L 78.4352 39.976L 78.4352 42L 69.2832 42L 69.2832 39.976L 72.8912 39.976L 72.8912 13.752L 69.2392 13.752L 69.2392 11.728L 75.3112 11.728L 75.3112 30.648L 78.9192 30.648L 83.8032 24.532L 80.5912 24.532L 80.5912 22.464L 89.5232 22.464L 89.5232 24.532L 86.3552 24.532L 80.8112 31.396L 87.5432 39.976L 90.5792 39.976L 90.5792 42Z"/> +<path id="path3_fill" fill-rule="evenodd" d="M 50.1196 19.2305C 50.7549 18.1952 51.125 17.5921 51.7513 18.064C 57.8821 22.6814 59.8811 33.9351 55.4308 42.8152C 54.4341 44.8042 53.1743 46.5352 51.704 48.0339C 49.7701 47.0253 48.2661 45.5184 47.5038 43.7364C 46.093 47.0338 42.143 49.3891 37.6288 49.3891C 33.1145 49.3891 29.1645 47.0338 27.7538 43.7364C 26.343 47.0338 22.393 49.3891 17.8788 49.3891C 13.3645 49.3891 9.41447 47.0338 8.00375 43.7364C 7.26579 45.4613 5.83299 46.9284 3.98805 47.9354C 2.12418 45.2647 1.03558 42.0543 1.03558 38.4553C 1.03558 31.2486 5.3735 25.052 11.5934 22.2921C 5.61858 20.0395 0 16.3055 0 12.5564C 0 8.60062 3.68587 9.3717 8.2905 10.335C 8.38057 10.3539 8.47099 10.3728 8.56175 10.3917C 9.80632 4.4583 15.096 0 21.4376 0C 28.703 0 34.5916 5.85301 34.5916 13.0724C 34.5916 17.64 32.2312 21.6552 28.6587 23.9928C 31.9238 25.0827 35.7856 25.9041 40.3394 25.3748C 46.8108 24.6225 48.9302 21.1689 50.1196 19.2305Z"/> +<path id="path4_fill" d="M 7.05374 3.53287C 7.05374 5.48402 5.4747 7.06574 3.52687 7.06574C 1.57903 7.06574 0 5.48402 0 3.53287C 0 1.58172 1.57903 0 3.52687 0C 5.4747 0 7.05374 1.58172 7.05374 3.53287Z"/> +<path id="path5_fill" d="M 5.26992 15.2297C 4.44439 16.8291 3.88077 18.4665 3.52634 20.1083L 8.37531 42.9269C 8.79147 43.7115 8.912 43.9574 8.5 43.5137L 8.37531 42.9269C 6.85509 40.0607 1.3898 30.005 3.52634 20.1083L 0 3.5137L 12.7169 0C 19.3717 5.11528 19.4225 7.50551 18.8281 16.3028C 11.1147 18.292 8.0235 9.89461 5.26992 15.2297Z"/> +<path id="path6_fill" d="M 51.7513 18.064C 49.9525 16.7087 50.2674 24.2207 40.3395 25.3748C 35.7856 25.9041 31.9238 25.0827 28.6587 23.9928C 32.2312 21.6552 34.5916 17.64 34.5916 13.0724C 34.5916 5.85301 28.703 1.8757e-08 21.4376 1.8757e-08C 15.096 1.8757e-08 9.80632 4.4583 8.56176 10.3917C 3.83149 9.40368 -4.16361e-09 8.52324 -4.16361e-09 12.5564C -4.16361e-09 16.3055 5.61858 20.0395 11.5934 22.2921C 5.37351 25.052 1.03558 31.2486 1.03558 38.4553C 1.03558 48.2286 9.06296 55.1362 18.8443 56.152C 32.7329 57.5947 49.2091 55.231 55.4308 42.8152C 59.8811 33.9351 57.8821 22.6814 51.7513 18.064Z"/> +<path id="path7_fill" d="M 2 1C 2 1.55228 1.55228 2 1 2C 0.447715 2 0 1.55228 0 1C 0 0.447715 0.447715 0 1 0C 1.55228 0 2 0.447715 2 1Z"/> +<path id="path8_fill" fill-rule="evenodd" d="M 0 1.52588e-05L 0 4.86665C 1.41072 8.16408 5.36072 10.5194 9.875 10.5194C 14.3893 10.5194 18.3393 8.16408 19.75 4.86665C 21.1607 8.16408 25.1107 10.5194 29.625 10.5194C 34.1393 10.5194 38.0893 8.16408 39.5 4.86665C 40.9107 8.16408 44.8607 10.5194 49.375 10.5194C 53.8893 10.5194 57.8393 8.16408 59.25 4.86665C 60.6607 8.16408 64.6107 10.5194 69.125 10.5194C 73.6393 10.5194 77.5893 8.16408 79 4.86665L 79 0C 77.5893 3.29741 73.6393 5.65273 69.125 5.65273C 64.6107 5.65273 60.6607 3.29741 59.25 0C 57.8393 3.29741 53.8893 5.65273 49.375 5.65273C 44.8607 5.65273 40.9107 3.29741 39.5 0C 38.0893 3.29741 34.1393 5.65273 29.625 5.65273C 25.1107 5.65273 21.1607 3.29741 19.75 0C 18.3393 3.29742 14.3893 5.65273 9.875 5.65273C 5.36072 5.65273 1.41072 3.29742 0 1.52588e-05Z"/> +</defs> +</svg> diff --git a/src/assets/images/package.json b/src/assets/images/package.json deleted file mode 100644 index 0a5693e4ee3946b74a1a1e9632f9abb5ddbd5d35..0000000000000000000000000000000000000000 --- a/src/assets/images/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "dependencies": { - "moving-average": "^1.0.0" - } -} diff --git a/src/assets/images/webvr_cube.svg b/src/assets/images/webvr_cube.svg new file mode 100755 index 0000000000000000000000000000000000000000..4ccbc7fee2c47d58d80ff9b8c11b6007786508e6 --- /dev/null +++ b/src/assets/images/webvr_cube.svg @@ -0,0 +1,22 @@ +<svg width="30" height="35" viewBox="0 0 30 35" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<title>webvr_cube</title> +<desc>Created using Figma</desc> +<g id="Canvas" transform="translate(-269 391)"> +<g id="webvr_cube"> +<g id="Vector"> +<use xlink:href="#path0_fill" transform="translate(269.099 -382.765)" fill="#2F80ED"/> +</g> +<g id="Vector 4"> +<use xlink:href="#path1_fill" transform="translate(269 -391)" fill="#B8D5FC"/> +</g> +<g id="Vector 3"> +<use xlink:href="#path2_fill" transform="translate(283.882 -382.5)" fill="#6DA4ED"/> +</g> +</g> +</g> +<defs> +<path id="path0_fill" d="M 14.7831 8.69853L 0 0L 0 17.9118L 14.8325 26.7647L 14.7831 8.69853Z"/> +<path id="path1_fill" d="M 14.882 0L 30 8.5L 14.882 16.9338L 0 8.33823L 14.882 0Z"/> +<path id="path2_fill" d="M 15.1181 17.5L 0.0494418 26.5L 0.0494418 25.9338L 0 8.43381L 15.1181 0L 15.1181 17.5Z"/> +</defs> +</svg> diff --git a/src/react-components/2d-hud.css b/src/assets/stylesheets/2d-hud.css similarity index 79% rename from src/react-components/2d-hud.css rename to src/assets/stylesheets/2d-hud.css index 556431ac6ca824e0bbcf4cf4d5644e05be4dc309..a8324a5bb8b054c4da44f82aadfe96647c5cb45b 100644 --- a/src/react-components/2d-hud.css +++ b/src/assets/stylesheets/2d-hud.css @@ -30,14 +30,16 @@ width: 48px; height: 48px; background-size: 100%; - background-image: url(../assets/hud/avatar.jpg); + background-image: url(../hud/avatar.jpg); } :local(.mic) { display: flex; width: 48px; height: 48px; - mask: url(../assets/hud/unmuted.png); + -webkit-mask: url(../hud/unmuted.png); + -webkit-mask-size: 48px; + mask: url(../hud/unmuted.png); mask-size: 48px; background-color: white; cursor: pointer; @@ -56,7 +58,9 @@ } :local(.mic.muted) { - mask: url(../assets/hud/muted.png); + -webkit-mask: url(../hud/muted.png); + -webkit-mask-size: 48px; + mask: url(../hud/muted.png); mask-size: 48px; } diff --git a/src/assets/stylesheets/audio.scss b/src/assets/stylesheets/audio.scss index 93e819f7a356f8b946c1292ff0ccddb5c0c03d5a..67aac69962d861e993fb0e58e5dec9441536d2a9 100644 --- a/src/assets/stylesheets/audio.scss +++ b/src/assets/stylesheets/audio.scss @@ -4,103 +4,103 @@ flex: 10 1 auto; justify-content: flex-start; align-items: center; -} - -.audio-setup-panel__title { - @extend %top-title; -} - -.audio-setup-panel__subtitle { - @extend %top-subtitle; -} - -.audio-setup-panel__device-chooser { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - position: relative; -} - -.audio-setup-panel__enter-button { - @extend %bottom-button; -} - -.audio-setup-panel__device-chooser__dropdown { - @extend %rounded-border; - @extend %default-font; - - background-color: black; - padding: 6px; - color: white; - font-size: 1.1em; - width: 90%; -} - -.audio-setup-panel__device-chooser__mic-icon { - position: absolute; - left: 7.5%; - top: 10px; -} - -.audio-setup-panel__levels { - margin-top: 20px; - margin-bottom: 20px; - display: flex; - justify-content: space-evenly; - align-items: center; - width: 100%; -} - -.audio-setup-panel__levels__mic { - position:relative; - width: 111px; - height: 111px; -} - -.audio-setup-panel__levels__mic_icon { - position: absolute; - top: 0; - left: 0; - z-index: 2; - min-width: 111px; - min-height: 111px; -} - -.audio-setup-panel__levels__speaker { - position:relative; - width: 111px; - height: 111px; -} - -.audio-setup-panel__levels__speaker_icon { - position: absolute; - top: 0; - left: 0; - z-index: 2; - min-width: 111px; - min-height: 111px; -} - -.audio-setup-panel__levels__level { - position: absolute; - top: 0; - left: 0; - opacity: 1.0; - z-index: 1; -} - -.audio-setup-panel__hmd-mic-warning { - margin: 20px; -} - -.audio-setup-panel__hmd-mic-warning__label { - vertical-align: middle; - margin-left: 5px; -} - -.audio-setup-panel__hmd-mic-warning__icon { - vertical-align: middle; + + &__enter-button { + @extend %bottom-button; + } + + &__title { + @extend %top-title; + } + + &__subtitle { + @extend %top-subtitle; + } + + &__device-chooser { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + position: relative; + + &__dropdown { + @extend %rounded-border; + @extend %default-font; + + background-color: black; + padding: 6px; + color: white; + font-size: 1.1em; + width: 90%; + } + + &__mic-icon { + position: absolute; + left: 7.5%; + top: 10px; + } + } + + &__levels { + margin-top: 20px; + margin-bottom: 20px; + display: flex; + justify-content: space-evenly; + align-items: center; + width: 100%; + + &__mic { + position:relative; + width: 111px; + height: 111px; + } + + &__mic_icon { + position: absolute; + top: 0; + left: 0; + z-index: 2; + min-width: 111px; + min-height: 111px; + } + + &__speaker { + position:relative; + width: 111px; + height: 111px; + } + + &__speaker_icon { + position: absolute; + top: 0; + left: 0; + z-index: 2; + min-width: 111px; + min-height: 111px; + } + + &__level { + position: absolute; + top: 0; + left: 0; + opacity: 1.0; + z-index: 1; + } + } + + &__hmd-mic-warning { + margin: 20px; + + &__label { + vertical-align: middle; + margin-left: 5px; + } + + &__icon { + vertical-align: middle; + } + } } .mic-grant-panel { @@ -109,26 +109,26 @@ flex: 10 1 auto; justify-content: flex-start; align-items: center; -} - -.mic-grant-panel__title { - @extend %top-title; -} - -.mic-grant-panel__subtitle { - @extend %top-subtitle; -} - -.mic-grant-panel__icon { - flex: 10; - display: flex; - justify-content: center; - align-items: center; - cursor: pointer; -} -.mic-grant-panel__next { - @extend %bottom-button; - margin: auto; - flex: 1 1 20px; + &__title { + @extend %top-title; + } + + &__subtitle { + @extend %top-subtitle; + } + + &__icon { + flex: 10; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + } + + &__next { + @extend %bottom-button; + margin: auto; + flex: 1 1 20px; + } } diff --git a/src/assets/stylesheets/avatar-selector.scss b/src/assets/stylesheets/avatar-selector.scss index 1663ec5b6ba92ef81c1b7fc9955f98dd0788272b..b3d20bea02eb7e17cebbbf220794aa5b3c305338 100644 --- a/src/assets/stylesheets/avatar-selector.scss +++ b/src/assets/stylesheets/avatar-selector.scss @@ -1,4 +1,3 @@ -@import 'fonts'; @import 'shared'; #selector-root { @@ -17,16 +16,10 @@ color: grey; } &__previous-button, &__next-button { + @extend %big-icon-button; position: absolute; top: 50%; margin-top: -0.5em; - appearance: none; - -moz-appearance: none; - -webkit-appearance: none; - background: transparent; - color: white; - border: none; - font-size: 64pt; } &__previous-button { left: 0.2em; diff --git a/src/assets/stylesheets/entry.scss b/src/assets/stylesheets/entry.scss index 2915f56da8f98f3fc79bda2b147d3750179c5dbc..bdd20d1ee7af717ac0b2808b696c85fe49c8d775 100644 --- a/src/assets/stylesheets/entry.scss +++ b/src/assets/stylesheets/entry.scss @@ -36,19 +36,20 @@ vertical-align: sub; margin: 0 0.6em } + &__screen-sharing-checkbox:checked { border: 9px double white; outline: 9px solid white; outline-offset: -18px; } -} -.entry-panel__secondary { - width: 100%; - text-align: center; - margin-top: 10px; - cursor: pointer; - color: $grey-text; + &__secondary { + width: 100%; + text-align: center; + margin-top: 10px; + cursor: pointer; + color: $grey-text; + } } .entry-button { @@ -57,20 +58,19 @@ margin-top: 10px; margin-bottom: 10px; cursor: pointer; -} -.entry-button__icon { - flex: 1 1 90px; - min-width: 90px; - min-height: 90px; -} + &__icon { + flex: 1 1 90px; + min-width: 90px; + min-height: 90px; + } -.entry-button__label { - flex: 10 1 auto; - margin-left: 20px; - font-size: 1.5em; - display: flex; - flex-direction: column; - justify-content: center; + &__label { + flex: 10 1 auto; + margin-left: 20px; + font-size: 1.5em; + display: flex; + flex-direction: column; + justify-content: center; + } } - diff --git a/src/assets/stylesheets/exited.scss b/src/assets/stylesheets/exited.scss index 341d382fecbe94680d7ffe8a4709c484074028f3..72959090e6cf5ed18d29c2750beb7bbb4280fcae 100644 --- a/src/assets/stylesheets/exited.scss +++ b/src/assets/stylesheets/exited.scss @@ -6,14 +6,14 @@ justify-content: center; align-items: center; flex-direction: column; -} -.exited-panel__title { - font-size: 1.2em; -} + &__title { + font-size: 1.2em; + } -.exited-panel__subtitle { - font-size: 0.8em; + &__subtitle { + font-size: 0.8em; + } } .autoexit-panel { @@ -23,19 +23,19 @@ justify-content: flex-start; align-items: center; padding: 15px; -} -.autoexit-panel__title { - @extend %top-title; -} - -.autoexit-panel__subtitle { - @extend %top-subtitle; -} + &__title { + @extend %top-title; + } + + &__subtitle { + @extend %top-subtitle; + } -.autoexit-panel__cancel-button { - @extend %bottom-button; - margin-top: 20px; - cursor: pointer; + &__cancel-button { + @extend %bottom-button; + margin-top: 20px; + cursor: pointer; + } } diff --git a/src/assets/stylesheets/fonts.scss b/src/assets/stylesheets/fonts.scss deleted file mode 100644 index 1ec1b6c12a0854b202b74f2a2f3d60ce9a3d517b..0000000000000000000000000000000000000000 --- a/src/assets/stylesheets/fonts.scss +++ /dev/null @@ -1,17 +0,0 @@ -/* latin-ext */ -@font-face { - font-family: 'Zilla Slab'; - font-style: normal; - font-weight: 400; - src: local('Zilla Slab'), local('ZillaSlab-Regular'), url('../fonts/zilla-slab_latin-ext.woff2') format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} - -/* latin */ -@font-face { - font-family: 'Zilla Slab'; - font-style: normal; - font-weight: 400; - src: local('Zilla Slab'), local('ZillaSlab-Regular'), url('../fonts/zilla-slab_latin.woff2') format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} diff --git a/src/assets/stylesheets/hub-create.scss b/src/assets/stylesheets/hub-create.scss new file mode 100644 index 0000000000000000000000000000000000000000..001ca379bd533abff1765aa117b549037077a2c5 --- /dev/null +++ b/src/assets/stylesheets/hub-create.scss @@ -0,0 +1,229 @@ +.create-panel { + display: flex; + flex-direction: column; + + @media (max-width: 768px) { + align-items: center; + } + + &__header { + color: $grey-text; + font-size: 1.2em; + margin-bottom: 14px; + margin-left: 12px; + text-shadow: 0px 0px 2px rgba(32, 32, 32, 1.0); + + @media (max-width: 768px) { + text-align: center; + } + } + + &__form { + position: relative; + width: 700px; + + @media (max-width: 768px) , (max-height: 715px) { + width: auto; + width: 500px; + } + + @media (max-width: 520px) { + width: 300px; + } + + &__name { + @extend %rounded-border; + @extend %default-font; + + color: white; + font-size: 2em; + background: black; + padding: 25px; + padding-left: 90px; + padding-right: 90px; + width: 100%; + line-height: 40px; + + &::selection { + background-color: #2F80ED; + color: white; + } + + &::-moz-selection { + background-color: #2F80ED; + color: white; + } + + @media (max-width: 768px) , (max-height: 715px) { + min-width: auto; + font-size: 1.5em; + width: 100%; + text-align: center; + } + + @media (max-width: 520px) { + padding-left: 15px; + font-size: 1.2em; + } + + &--expanded { + margin-top: 340px; + border-radius: 0px 0px 14px 14px; + + @media (max-width: 768px) , (max-height: 715px) { + margin-top: 275px; + } + + @media (max-width: 520px) { + margin-top: 165px; + } + } + } + + &__submit-button , &__rotate-button { + appearance: none; + -moz-appearance: none; + -webkit-appearance: none; + background: transparent; + border: none; + cursor: pointer; + } + + &__left-container { + position: absolute; + height: 100%; + width: 90px; + height: 95px; + left: 4px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + bottom: 0; + + @media (max-width: 520px) { + display: none; + } + } + + &__right-container { + position: absolute; + height: 95px; + width: 90px; + right: 4px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + bottom: 0; + } + + &__environment { + position: absolute; + top: 0px; + left: 0px; + width: 700px; + height: 100%; + box-sizing: border-box; + border-radius: 14px; + border: 3px solid white; + border-radius: 14px; + overflow: hidden; + pointer-events: none; + + @media (max-width: 768px) , (max-height: 715px) { + width: 100%; + } + + &__picker { + width: 100%; + height: 340px; + border-bottom: 2px solid white; + overflow: hidden; + position: relative; + + @media (max-width: 768px) , (max-height: 715px) { + height: 280px; + } + + @media (max-width: 520px) { + height: 165px; + } + + &__image { + width: 112%; + position: absolute; + top: -3%; + left: -3%; + } + + &__labels { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + + &__footer { + position: absolute; + bottom: 14px; + left: 12px; + font-size: 1.2em; + text-shadow: 0px 0px 6px #202020; + color: $light-text; + } + + &__header { + position: absolute; + top: 14px; + left: 18px; + display: flex; + flex-direction: column; + text-shadow: 0px 0px 3px #404040; + + &__title { + font-size: 1.6em; + font-weight: bold; + color: white; + } + + &__author { + color: $light-text; + font-size: 1.4em; + + @media (max-width: 520px) { + display: none; + } + } + } + } + + &__controls { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + + &__next, &__prev { + @extend %big-icon-button; + + position: absolute; + top: 50%; + margin-top: -0.5em; + pointer-events: auto; + cursor: pointer; + } + + &__prev { + left: 0.2em; + } + + &__next { + right: 0.2em; + } + } + } + } + } +} diff --git a/src/assets/stylesheets/room.scss b/src/assets/stylesheets/hub.scss similarity index 94% rename from src/assets/stylesheets/room.scss rename to src/assets/stylesheets/hub.scss index d042519b31409aa888dde3bbe83e69758fb1cb54..c10db8dd196ca6b3dce999605e002893cacd05e6 100644 --- a/src/assets/stylesheets/room.scss +++ b/src/assets/stylesheets/hub.scss @@ -1,4 +1,3 @@ -@import 'fonts'; @import 'shared'; @import 'loader'; diff --git a/src/assets/stylesheets/index.scss b/src/assets/stylesheets/index.scss new file mode 100644 index 0000000000000000000000000000000000000000..77615e9d341f4ca5688ebe6f0e209163f2c884cf --- /dev/null +++ b/src/assets/stylesheets/index.scss @@ -0,0 +1,219 @@ +@import 'shared'; +@import 'hub-create'; + +* { + box-sizing: border-box; +} + +body { + margin: 0; + padding: 0; + background-color: black; + color: white; +} + +.home-root { + @extend %default-font; + + width: 100%; + height: 100%; + margin: 0; + padding: 0; +} + +.home { + position: absolute; + display: flex; + width: 100%; + height: 100%; +} + +.main-content { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + z-index: 2; +} + +.background-video { + position: fixed; + top: 0; + left: 0; + opacity: 0.66; + min-width: 100%; + min-height: 100%; + z-index: 1; +} + +.header-content { + padding: 1.5em 2.5em 1.5em 2.5em; + background-color: rgba(0, 0, 0, 0.85); + min-height: 90px; + display: flex; + border-bottom: 2px solid #242424; + + &__title { + flex: 10; + display: flex; + + @media (max-width: 768px) { + justify-content: center; + } + + &__name { + width: 300px; + } + + &__preview { + color: $grey-text; + margin-left: 10px; + } + } + + &__experiment { + text-align: right; + flex: 1 1 350px; + color: $grey-text; + font-size: 1.0em; + font-weight: lighter; + + @media (max-width: 768px) { + display: none; + } + + &__container { + display: flex; + justify-content: flex-end; + } + + &__icon { + margin-left: 12px; + margin-right: 12px; + } + + &__info { + display: flex; + flex-direction: column; + justify-content: center; + + &__header { + a { + color: $grey-text; + } + } + + &__link { + color: $dark-grey; + font-size: 0.8em; + } + } + } +} + +.hero-content { + flex: 10; + min-height: 740px; + display: flex; + flex-direction: column; + + @media (max-width: 768px) { + padding: 1em 1.5em 1em 1.5em; + justify-content: space-around; + min-height: 490px; + } + + &__container { + padding-top: 2vw; + padding-left: 2.1em; + padding-right: 2.1em; + flex: 10; + text-shadow: 0px 0px 2px rgba(32, 32, 32, 1.0); + + @media (max-height: 812px) , (max-width: 768px) { + flex: 0 0 auto; + } + + @media (max-height: 720px) { + padding-bottom: 0px; + } + + &__title { + font-size: 4vw; + font-weight: bold; + + @media (max-width: 768px) , (max-height: 715px) { + font-size: 1.9em; + } + + @media (max-width: 768px) { + text-align: center; + } + + @media (min-width: 1824px) { + font-size: 4.5em; + } + } + + &__subtitle { + font-size: 2.5vw; + font-weight: lighter; + color: $light-text; + + @media (max-width: 768px) , (max-height: 715px) { + font-size: 1.1em; + margin-top: 0.2em; + } + + @media (max-width: 768px) { + text-align: center; + } + + @media (min-width: 1824px) { + font-size: 2.8em; + } + } + } + + &__create { + padding: 2.1em; + padding-bottom: 3.5vw; + + @media (max-width: 768px) { + padding: 0.5em; + align-self: center; + } + } +} + +.footer-content { + padding: 1em 2.25em 1em 2.25em; + background-color: rgba(0, 0, 0, 0.85); + min-height: 75px; + display: flex; + border-top: 2px solid #242424; + align-items: center; + justify-content: center; + + &__links { + text-align: center; + color: $dark-grey; + display: flex; + flex-direction: column; + + &__top { + display: flex; + justify-content: space-between; + } + + &__link { + color: $grey-text; + margin-left: 8px; + margin-right: 8px; + } + + &__bottom { + margin-top: 4px; + } + } +} diff --git a/src/assets/stylesheets/loader.scss b/src/assets/stylesheets/loader.scss index 400636ca8f4b7fb541ed62359b8f0b30238bb63e..a770b54d46a7a4126c69017b89257e343d66c4ce 100644 --- a/src/assets/stylesheets/loader.scss +++ b/src/assets/stylesheets/loader.scss @@ -5,6 +5,12 @@ } .loading-panel { + @extend %default-font; + pointer-events: none; + color: white; + position: absolute; + top: 0; + left: 0; background-color: black; width: 100%; height: 100%; diff --git a/src/assets/stylesheets/profile.scss b/src/assets/stylesheets/profile.scss index 9b45afd389dd5006d1596ee11d94f41a1ee59650..000e974bbad3aec3760eb51e9863c78755a590fc 100644 --- a/src/assets/stylesheets/profile.scss +++ b/src/assets/stylesheets/profile.scss @@ -15,83 +15,84 @@ height: 100%; margin: 1em 0; } -} -.profile-entry__box { - border-radius: 8px; - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - padding: 15px; - flex: 1 1 100%; - width: 60vw; - min-width: 300px; - max-width: 700px; - height: 500px -} + &__box { + border-radius: 8px; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + padding: 15px; + flex: 1 1 100%; + width: 60vw; + min-width: 300px; + max-width: 700px; + height: 500px; -.profile-entry__box--darkened { - background-color: $darkest-transparent; -} + &--darkened { + background-color: $darkest-transparent; + } + } -.profile-entry__subtitle { - width: 100%; - text-align: center; - font-size: 1.2em; - color: $grey-text; -} + &__subtitle { + width: 100%; + text-align: center; + font-size: 1.2em; + color: $grey-text; + } -.profile-entry__form-field-text { - @extend %rounded-border; - @extend %default-font; + &__form-field-text { + @extend %rounded-border; + @extend %default-font; - color: $light-text; - font-size: 1.2em; - background-color: transparent; - line-height: 2.0em; - padding-left: 1.25em; - padding-right: 1.25em; - margin: 0.5em 0; -} + color: $light-text; + font-size: 1.2em; + background-color: transparent; + line-height: 2.0em; + padding-left: 1.25em; + padding-right: 1.25em; + margin: 0.5em 0; + } -.profile-entry__form-submit { - @extend %default-font; - border: none; + &__form-submit { + @extend %default-font; + border: none; - margin: 8px; - width: 100px; - line-height: 1.5em; - font-size: 1.0em; + margin: 8px; + width: 100px; + line-height: 1.5em; + font-size: 1.0em; - background-color: transparent; - font-weight: bold; - color: white; - cursor: pointer; + background-color: transparent; + font-weight: bold; + color: white; + cursor: pointer; + } } .profile-info-header { display: flex; flex: 1 1 40px; width: 100%; -} -.profile-info-header__icon { - cursor: pointer; - width: 20px; - height: 20px; - padding: 15px; -} + &__icon { + cursor: pointer; + width: 20px; + height: 20px; + padding: 15px; + } -.profile-info-header__profile_display_name { - cursor: pointer; - flex: 6 1 auto; - font-size: 1.2em; - line-height: 50px; -} + &__profile_display_name { + cursor: pointer; + flex: 6 1 auto; + font-size: 1.2em; + line-height: 50px; + } -.profile-info-header__app_name { - font-size: 1.8em; - padding-right: 18px; - line-height: 50px; + &__app_name { + font-size: 1.8em; + padding-right: 18px; + line-height: 50px; + } } + diff --git a/src/assets/stylesheets/shared.scss b/src/assets/stylesheets/shared.scss index a353915f5e8ec2e09a5c9ff79cadbd5aecd1732d..c2d4f013de5b853664b751400969602b356a53f9 100644 --- a/src/assets/stylesheets/shared.scss +++ b/src/assets/stylesheets/shared.scss @@ -33,3 +33,12 @@ $darker-grey: rgba(64, 64, 64, 1.0); padding-top: 4px; } +%big-icon-button { + appearance: none; + -moz-appearance: none; + -webkit-appearance: none; + background: transparent; + color: white; + border: none; + font-size: 64pt; +} diff --git a/src/assets/stylesheets/ui-root.scss b/src/assets/stylesheets/ui-root.scss index 74f4ad4a37f344162f1ee029a51fd5e0525e2728..debc2b188557353ab7ec2e081347e826d5a192c0 100644 --- a/src/assets/stylesheets/ui-root.scss +++ b/src/assets/stylesheets/ui-root.scss @@ -20,10 +20,10 @@ grid-template-rows: 1fr 20px minmax(400px, 600px) 20px 1fr; width: 100%; height: 100%; -} -.ui-dialog--darkened { - background-color: $dark-transparent; + &--darkened { + background-color: $dark-transparent; + } } .ui-dialog-box { @@ -37,17 +37,14 @@ border-radius: 8px; width: 100%; height: 100%; + + &--backgrounded { + filter: blur(1px); + opacity: 0.7; + pointer-events: none; + } } .ui-interactive { pointer-events: auto; } - -.ui-dialog-box--backgrounded { -} - -.ui-dialog-box-contents--backgrounded { - filter: blur(1px); - opacity: 0.7; - pointer-events: none; -} diff --git a/src/assets/translations.data.json b/src/assets/translations.data.json index 4c9af592fa7b15592af0961f4df84aaf3b1fe2d5..1757ec61cce5b97855750537a7812efe5349a57e 100644 --- a/src/assets/translations.data.json +++ b/src/assets/translations.data.json @@ -32,6 +32,20 @@ "autoexit.title": "Auto-ending session in ", "autoexit.title_units": " seconds", "autoexit.subtitle": "You have started another session.", - "autoexit.cancel": "CANCEL" + "autoexit.cancel": "CANCEL", + "home.create_header": "Name your room:", + "home.environment_picker_footer": "Choose a scene", + "home.create_name.validation_warning": "Invalid name, limited to 4 to 64 characters and limited symbols.", + "home.webvr_disclaimer_pre": "A ", + "home.webvr_disclaimer_post": " experiment by ", + "home.webvr_disclaimer_mr_team": "Mozilla Mixed Reality", + "home.view_source": "View Source", + "home.join_on_slack": "Join us on Slack", + "home.report_issue": "Report an Issue", + "home.get_updates": "Get Updates", + "home.hero_title": "A new way to get together online.", + "home.hero_subtitle": "Laugh, play, get stuff done, or just hang out.", + "home.made_with_love": "made with â¤ï¸ by ", + "home.environment_author_by": " by " } } diff --git a/src/assets/video/home.webm b/src/assets/video/home.webm new file mode 100644 index 0000000000000000000000000000000000000000..d7156cad9530c12a8b0b3f31332cfebda0f4cea9 Binary files /dev/null and b/src/assets/video/home.webm differ diff --git a/src/avatar-selector.js b/src/avatar-selector.js index 6acfe154caebb1c9f799631940aeb918e945fd7e..9f5aabab2417584d33a1d6eb43f75503a0212631 100644 --- a/src/avatar-selector.js +++ b/src/avatar-selector.js @@ -1,7 +1,7 @@ import ReactDOM from "react-dom"; import React from "react"; import queryString from "query-string"; -import { IntlProvider, FormattedMessage, addLocaleData } from "react-intl"; +import { IntlProvider, addLocaleData } from "react-intl"; import en from "react-intl/locale-data/en"; import "./assets/stylesheets/avatar-selector.scss"; @@ -10,15 +10,16 @@ import "./vendor/GLTFLoader"; import "./components/animation-mixer"; import "./components/audio-feedback"; import "./components/loop-animation"; -import "./elements/a-progressive-asset"; import "./gltf-component-mappings"; -import { avatars } from "./assets/avatars/avatars.js"; -import { avatarIds } from "./utils/identity"; +import { avatars } from "./assets/avatars/avatars"; +import registerTelemetry from "./telemetry"; import { App } from "./App"; import AvatarSelector from "./react-components/avatar-selector"; import localeData from "./assets/translations.data.json"; +registerTelemetry(); + window.APP = new App(); const hash = queryString.parse(location.hash); const isMobile = AFRAME.utils.device.isMobile(); diff --git a/src/behaviours/oculus-touch-joystick-dpad4.js b/src/behaviours/oculus-touch-joystick-dpad4.js index 758fb603c70faceb932539d722e08976e603046d..bf397ba2fbda2357c7fdf1cfc0310c8aa1ba548c 100644 --- a/src/behaviours/oculus-touch-joystick-dpad4.js +++ b/src/behaviours/oculus-touch-joystick-dpad4.js @@ -1,4 +1,4 @@ -import { angleTo4Direction, angleTo8Direction } from "../utils/dpad"; +import { angleTo4Direction } from "../utils/dpad"; // @TODO specify 4 or 8 direction function oculus_touch_joystick_dpad4(el, outputPrefix) { @@ -15,11 +15,8 @@ oculus_touch_joystick_dpad4.prototype = { emitDPad4: function(event) { const x = event.detail.axis[0]; const y = event.detail.axis[1]; - const inCenter = - Math.abs(x) < this.centerRadius && Math.abs(y) < this.centerRadius; - const current = inCenter - ? "center" - : this.angleToDirection(Math.atan2(x, -y)); + const inCenter = Math.abs(x) < this.centerRadius && Math.abs(y) < this.centerRadius; + const current = inCenter ? "center" : this.angleToDirection(Math.atan2(x, -y)); if (current !== this.previous) { this.previous = current; event.target.emit(`${this.outputPrefix}_dpad4_${current}`); diff --git a/src/behaviours/vive-trackpad-dpad4.js b/src/behaviours/vive-trackpad-dpad4.js index 3f4e3a275a4c6bad6a74aa6ef3c8836bb3a26500..99bdf8873d4cbcce3541691e6073af71c153db2b 100644 --- a/src/behaviours/vive-trackpad-dpad4.js +++ b/src/behaviours/vive-trackpad-dpad4.js @@ -1,4 +1,4 @@ -import { angleTo4Direction, angleTo8Direction } from "../utils/dpad"; +import { angleTo4Direction } from "../utils/dpad"; function vive_trackpad_dpad4(el, outputPrefix) { this.outputPrefix = outputPrefix; @@ -16,20 +16,17 @@ function vive_trackpad_dpad4(el, outputPrefix) { } vive_trackpad_dpad4.prototype = { - press: function(_) { + press: function() { this.pressed = true; }, - unpress: function(_) { + unpress: function() { this.pressed = false; }, emitDPad4: function(event) { const x = event.detail.axis[0]; const y = event.detail.axis[1]; - const inCenter = - Math.abs(x) < this.centerRadius && Math.abs(y) < this.centerRadius; - const direction = inCenter - ? "center" - : angleTo4Direction(Math.atan2(x, -y)); + const inCenter = Math.abs(x) < this.centerRadius && Math.abs(y) < this.centerRadius; + const direction = inCenter ? "center" : angleTo4Direction(Math.atan2(x, -y)); const pressed = this.pressed ? "pressed_" : ""; const current = `${pressed + direction}`; // e.g. "pressed_north" diff --git a/src/components/audio-feedback.js b/src/components/audio-feedback.js index 29aafcdc45bd1fb8838b4824a9c75f1c2f471c9e..d4bdf5792e79cc1eb3191a0d042f59e89308956d 100644 --- a/src/components/audio-feedback.js +++ b/src/components/audio-feedback.js @@ -77,6 +77,9 @@ AFRAME.registerComponent("scale-audio-feedback", { }, onAudioFrequencyChange(e) { + // TODO: come up with a cleaner way to handle this. + // bone's are "hidden" by scaling them with bone-visibility, without this we would overwrite that. + if (!this.el.object3D.visible) return; const { minScale, maxScale } = this.data; this.el.object3D.scale.setScalar(minScale + (maxScale - minScale) * e.detail.volume / 255); } diff --git a/src/components/bone-mute-state-indicator.js b/src/components/bone-mute-state-indicator.js index 7c6dc5913e971a3a3981f2d8af7ed3e151dcf0e8..2d79b3f9c37feee028b89afbe6eaffe7bbd654d8 100644 --- a/src/components/bone-mute-state-indicator.js +++ b/src/components/bone-mute-state-indicator.js @@ -12,12 +12,8 @@ AFRAME.registerComponent("bone-mute-state-indicator", { init() { this.onStateToggled = this.onStateToggled.bind(this); this.el.addEventListener("model-loaded", () => { - this.unmutedBone = this.el.object3D.getObjectByName( - this.data.unmutedBoneName - ); - this.mutedBone = this.el.object3D.getObjectByName( - this.data.mutedBoneName - ); + this.unmutedBone = this.el.object3D.getObjectByName(this.data.unmutedBoneName); + this.mutedBone = this.el.object3D.getObjectByName(this.data.mutedBoneName); console.log(this.unmutedBone, this.mutedBone); this.modelLoaded = true; diff --git a/src/components/bone-visibility.js b/src/components/bone-visibility.js new file mode 100644 index 0000000000000000000000000000000000000000..6f6f1a53e6f3eb464561ea91741d6d09e33a093f --- /dev/null +++ b/src/components/bone-visibility.js @@ -0,0 +1,16 @@ +AFRAME.registerComponent("bone-visibility", { + tick() { + const { visible } = this.el.object3D; + + if (this.lastVisible !== visible) { + if (visible) { + this.el.object3D.scale.set(1, 1, 1); + } else { + // Three.js doesn't like updating matrices with 0 scale, so we set it to a near zero number. + this.el.object3D.scale.set(0.00000001, 0.00000001, 0.00000001); + } + + this.lastVisible = visible; + } + } +}); diff --git a/src/components/character-controller.js b/src/components/character-controller.js index ae49b6ee7a127eedcff70353576d2255dea3d2ce..3f72a7160d0f0d3af7e410e82c0dabb68ea244f3 100644 --- a/src/components/character-controller.js +++ b/src/components/character-controller.js @@ -61,11 +61,11 @@ AFRAME.registerComponent("character-controller", { this.angularVelocity = event.detail.value; }, - snapRotateLeft: function(event) { + snapRotateLeft: function() { this.pendingSnapRotationMatrix.copy(this.leftRotationMatrix); }, - snapRotateRight: function(event) { + snapRotateRight: function() { this.pendingSnapRotationMatrix.copy(this.rightRotationMatrix); }, diff --git a/src/components/debug.js b/src/components/debug.js index 77202f03295be677b9156e1ff16f11100ab19351..6448bf236966876e05069f96192153cf0f8400b6 100644 --- a/src/components/debug.js +++ b/src/components/debug.js @@ -1,25 +1,30 @@ AFRAME.registerComponent("lifecycle-checker", { schema: { + name: { type: "string" }, tick: { default: false } }, init: function() { - console.log("init", this.el); + this.log("init"); }, update: function() { - console.log("update", this.el); + this.log("update"); }, tick: function() { if (this.data.tick) { - console.log("tick", this.el); + this.log("tick"); } }, remove: function() { - console.log("remove", this.el); + this.log("remove"); }, pause: function() { - console.log("pause", this.el); + this.log("pause"); }, play: function() { - console.log("play", this.el); + this.log("play"); + }, + + log: function(method) { + console.info(`lifecycle-checker:${this.data.name} ${method}`); } }); diff --git a/src/components/event-repeater.js b/src/components/event-repeater.js index f099038b48ca692f1e7443e650b59cdfaeb7481e..64e28720698d8aaa26a8f0ce1525301cebd1ee2f 100644 --- a/src/components/event-repeater.js +++ b/src/components/event-repeater.js @@ -26,5 +26,4 @@ AFRAME.registerComponent("event-repeater", { _handleEvent: function(event, e) { this.el.emit(event, e.details); } - }); diff --git a/src/components/gltf-bundle.js b/src/components/gltf-bundle.js new file mode 100644 index 0000000000000000000000000000000000000000..85238043fb2319dd1a6e55bc953186890fd2e721 --- /dev/null +++ b/src/components/gltf-bundle.js @@ -0,0 +1,29 @@ +AFRAME.registerComponent("gltf-bundle", { + schema: { + src: { default: "" } + }, + + init: async function() { + this._addGltfEntitiesForBundleJson = this._addGltfEntitiesForBundleJson.bind(this); + + const res = await fetch(this.data.src); + const data = await res.json(); + this._addGltfEntitiesForBundleJson(data); + }, + + _addGltfEntitiesForBundleJson: function(bundleJson) { + const loaded = []; + + for (let i = 0; i < bundleJson.assets.length; i++) { + const asset = bundleJson.assets[i]; + const src = asset.src; + const gltfEl = document.createElement("a-entity"); + gltfEl.setAttribute("gltf-model-plus", { src }); + gltfEl.setAttribute("position", "0 0 0"); + loaded.push(new Promise(resolve => gltfEl.addEventListener("model-loaded", resolve))); + this.el.appendChild(gltfEl); + } + + Promise.all(loaded).then(() => this.el.emit("bundleloaded")); + } +}); diff --git a/src/elements/a-gltf-entity.js b/src/components/gltf-model-plus.js similarity index 55% rename from src/elements/a-gltf-entity.js rename to src/components/gltf-model-plus.js index 33489e2bc1792aa2b9dd4bddca360caaa39c509b..3864f9327c92ce63deddf8f08766a9316cf4a3da 100644 --- a/src/elements/a-gltf-entity.js +++ b/src/components/gltf-model-plus.js @@ -1,6 +1,6 @@ const GLTFCache = {}; -AFRAME.AGLTFEntity = { +AFRAME.GLTFModelPlus = { defaultInflator(el, componentName, componentData) { if (!AFRAME.components[componentName]) { throw new Error(`Inflator failed. "${componentName}" component does not exist.`); @@ -14,8 +14,8 @@ AFRAME.AGLTFEntity = { } }, registerComponent(componentKey, componentName, inflator) { - AFRAME.AGLTFEntity.components[componentKey] = { - inflator: inflator || AFRAME.AGLTFEntity.defaultInflator, + AFRAME.GLTFModelPlus.components[componentKey] = { + inflator: inflator || AFRAME.GLTFModelPlus.defaultInflator, componentName }; }, @@ -128,7 +128,7 @@ const inflateEntities = function(parentEl, node) { if (entityComponents) { for (const prop in entityComponents) { if (entityComponents.hasOwnProperty(prop)) { - const { inflator, componentName } = AFRAME.AGLTFEntity.components[prop]; + const { inflator, componentName } = AFRAME.GLTFModelPlus.components[prop]; if (inflator) { inflator(el, componentName, entityComponents[prop]); @@ -166,7 +166,7 @@ function nextTick() { }); } -function cachedLoadGLTF(src, onProgress) { +function cachedLoadGLTF(src, preferredTechnique, onProgress) { return new Promise((resolve, reject) => { // Load the gltf model from the cache if it exists. if (GLTFCache[src]) { @@ -174,7 +174,10 @@ function cachedLoadGLTF(src, onProgress) { resolve(cloneGltf(GLTFCache[src])); } else { // Otherwise load the new gltf model. - new THREE.GLTFLoader().load( + const gltfLoader = new THREE.GLTFLoader(); + gltfLoader.preferredTechnique = preferredTechnique; + + gltfLoader.load( src, model => { if (!GLTFCache[src]) { @@ -190,138 +193,95 @@ function cachedLoadGLTF(src, onProgress) { }); } -AFRAME.registerElement("a-gltf-entity", { - prototype: Object.create(AFRAME.AEntity.prototype, { - load: { - async value() { - if (this.hasLoaded || !this.parentEl) { - return; - } - - // The code above and below this are from AEntity.prototype.load, we need to monkeypatch in gltf loading mid function - this.loadTemplates(); - await this.applySrc(this.getAttribute("src")); - // - - AFRAME.ANode.prototype.load.call(this, () => { - // Check if entity was detached while it was waiting to load. - if (!this.parentEl) { - return; - } +AFRAME.registerComponent("gltf-model-plus", { + schema: { + src: { type: "string" }, + inflate: { default: false }, + preferredTechnique: { default: AFRAME.utils.device.isMobile() ? "KHR_materials_unlit" : "pbrMetallicRoughness" } + }, - this.updateComponents(); - if (this.isScene || this.parentEl.isPlaying) { - this.play(); - } - }); - } - }, - - loadTemplates: { - value() { - this.templates = []; - this.querySelectorAll(":scope > template").forEach(templateEl => - this.templates.push({ - selector: templateEl.getAttribute("data-selector"), - templateRoot: document.importNode( - templateEl.firstElementChild || templateEl.content.firstElementChild, - true - ) - }) - ); - } - }, - - applySrc: { - async value(src) { - try { - // If the src attribute is a selector, get the url from the asset item. - if (src && src.charAt(0) === "#") { - const assetEl = document.getElementById(src.substring(1)); - if (!assetEl) { - console.warn(`Attempted to use non-existent asset ${src} as src for`, this); - return; - } - - const fallbackSrc = assetEl.getAttribute("src"); - const highSrc = assetEl.getAttribute("high-src"); - const lowSrc = assetEl.getAttribute("low-src"); - - if (highSrc && window.APP.quality === "high") { - src = highSrc; - } else if (lowSrc && window.APP.quality === "low") { - src = lowSrc; - } else { - src = fallbackSrc; - } - } + init() { + this.loadTemplates(); + }, - if (src === this.lastSrc) return; - this.lastSrc = src; + update() { + this.applySrc(this.data.src); + }, - if (!src) { - if (this.inflatedEl) { - console.warn("gltf-entity set to an empty source, unloading inflated model."); - this.removeInflatedEl(); - } - return; - } + loadTemplates() { + this.templates = []; + this.el.querySelectorAll(":scope > template").forEach(templateEl => + this.templates.push({ + selector: templateEl.getAttribute("data-selector"), + templateRoot: document.importNode(templateEl.firstElementChild || templateEl.content.firstElementChild, true) + }) + ); + }, - const model = await cachedLoadGLTF(src); + async applySrc(src) { + try { + // If the src attribute is a selector, get the url from the asset item. + if (src && src.charAt(0) === "#") { + const assetEl = document.getElementById(src.substring(1)); + + const fallbackSrc = assetEl.getAttribute("src"); + const highSrc = assetEl.getAttribute("high-src"); + const lowSrc = assetEl.getAttribute("low-src"); + + if (highSrc && window.APP.quality === "high") { + src = highSrc; + } else if (lowSrc && window.APP.quality === "low") { + src = lowSrc; + } else { + src = fallbackSrc; + } + } - // If we started loading something else already - // TODO: there should be a way to cancel loading instead - if (src != this.lastSrc) return; + if (src === this.lastSrc) return; + this.lastSrc = src; - // If we had inflated something already before, clean that up + if (!src) { + if (this.inflatedEl) { + console.warn("gltf-model-plus set to an empty source, unloading inflated model."); this.removeInflatedEl(); + } + return; + } - this.model = model.scene || model.scenes[0]; - this.model.animations = model.animations; + const model = await cachedLoadGLTF(src, this.data.preferredTechnique); - this.setObject3D("mesh", this.model); + // If we started loading something else already + // TODO: there should be a way to cancel loading instead + if (src != this.lastSrc) return; - if (this.getAttribute("inflate")) { - this.inflatedEl = inflateEntities(this, this.model); - // TODO: Still don't fully understand the lifecycle here and how it differs between browsers, we should dig in more - // Wait one tick for the appended custom elements to be connected before attaching templates - await nextTick(); - if (src != this.lastSrc) return; // TODO: there must be a nicer pattern for this - this.templates.forEach(attachTemplate.bind(null, this)); - } + // If we had inflated something already before, clean that up + this.removeInflatedEl(); - this.emit("model-loaded", { format: "gltf", model: this.model }); - } catch (e) { - console.error("Failed to load glTF model", e.message, this); - this.emit("model-error", { format: "gltf", src }); - } - } - }, + this.model = model.scene || model.scenes[0]; + this.model.animations = model.animations; - removeInflatedEl: { - value() { - if (this.inflatedEl) { - this.inflatedEl.parentNode.removeChild(this.inflatedEl); - delete this.inflatedEl; - } - } - }, + this.el.setObject3D("mesh", this.model); - attributeChangedCallback: { - value(attr, oldVal, newVal) { - if (attr === "src") { - this.applySrc(newVal); - } + if (this.data.inflate) { + this.inflatedEl = inflateEntities(this.el, this.model); + // TODO: Still don't fully understand the lifecycle here and how it differs between browsers, we should dig in more + // Wait one tick for the appended custom elements to be connected before attaching templates + await nextTick(); + if (src != this.lastSrc) return; // TODO: there must be a nicer pattern for this + this.templates.forEach(attachTemplate.bind(null, this.el)); } - }, - setAttribute: { - value(attr, arg1, arg2) { - if (attr === "src") { - this.applySrc(arg1); - } - AFRAME.AEntity.prototype.setAttribute.call(this, attr, arg1, arg2); - } + this.el.emit("model-loaded", { format: "gltf", model: this.model }); + } catch (e) { + console.error("Failed to load glTF model", e.message, this); + this.el.emit("model-error", { format: "gltf", src }); } - }) + }, + + removeInflatedEl() { + if (this.inflatedEl) { + this.inflatedEl.parentNode.removeChild(this.inflatedEl); + delete this.inflatedEl; + } + } }); diff --git a/src/components/haptic-feedback.js b/src/components/haptic-feedback.js index 1ef413d38939a183c25438694cebaec934d9cf23..35a79cd9555e2864e5c496f671fcb9ddd8831345 100644 --- a/src/components/haptic-feedback.js +++ b/src/components/haptic-feedback.js @@ -18,9 +18,9 @@ AFRAME.registerComponent("haptic-feedback", { }, getActuator() { - return new Promise((resolve, reject) => { + return new Promise(resolve => { const tryGetActivator = () => { - var trackedControls = this.el.components["tracked-controls"]; + const trackedControls = this.el.components["tracked-controls"]; if ( trackedControls && trackedControls.controller && @@ -44,7 +44,7 @@ AFRAME.registerComponent("haptic-feedback", { }, pulse: function(event) { - let { intensity } = event.detail; + const { intensity } = event.detail; if (!strengthForIntensity[intensity]) { console.warn(`Invalid intensity : ${intensity}`); return; diff --git a/src/components/ik-controller.js b/src/components/ik-controller.js index 0a1990bd8ede46f4e5bbdde5cfced182ed4f5a3d..23f2dbf799f59558fd261eaa615f1bd2d37a66d2 100644 --- a/src/components/ik-controller.js +++ b/src/components/ik-controller.js @@ -1,5 +1,4 @@ const { Vector3, Quaternion, Matrix4, Euler } = THREE; - AFRAME.registerComponent("ik-root", { schema: { camera: { type: "string", default: ".camera" }, @@ -67,16 +66,12 @@ AFRAME.registerComponent("ik-controller", { this.hands = { left: { - lastVisible: true, rotation: new Matrix4().makeRotationFromEuler(new Euler(-Math.PI / 2, Math.PI / 2, 0)) }, right: { - lastVisible: true, rotation: new Matrix4().makeRotationFromEuler(new Euler(Math.PI / 2, Math.PI / 2, 0)) } }; - - this.headLastVisible = true; }, update(oldData) { @@ -142,7 +137,6 @@ AFRAME.registerComponent("ik-controller", { cameraYRotation, cameraYQuaternion, invHipsQuaternion, - headQuaternion, leftHand, rightHand, rootToChest, @@ -181,14 +175,6 @@ AFRAME.registerComponent("ik-controller", { this.updateHand(this.hands.left, leftHand, leftController); this.updateHand(this.hands.right, rightHand, rightController); - - if (head.object3D.visible) { - if (!this.headLastVisible) { - head.object3D.scale.set(1, 1, 1); - } - } else if (this.headLastVisible) { - head.object3D.scale.set(0.0000001, 0.0000001, 0.0000001); - } }, updateHand(handState, hand, controller) { @@ -196,11 +182,13 @@ AFRAME.registerComponent("ik-controller", { const handMatrix = handObject3D.matrix; const controllerObject3D = controller.object3D; + // TODO: This coupling with personal-space-invader is not ideal. + // There should be some intermediate thing managing multiple opinions about object visibility + const spaceInvader = hand.components["personal-space-invader"]; + const handHiddenByPersonalSpace = spaceInvader && spaceInvader.invading; + + handObject3D.visible = !handHiddenByPersonalSpace && controllerObject3D.visible; if (controllerObject3D.visible) { - if (!handState.lastVisible) { - handObject3D.scale.set(1, 1, 1); - handState.lastVisible = true; - } handMatrix.multiplyMatrices(this.invRootToChest, controllerObject3D.matrix); const handControls = controller.components["hand-controls2"]; @@ -213,11 +201,6 @@ AFRAME.registerComponent("ik-controller", { handObject3D.position.setFromMatrixPosition(handMatrix); handObject3D.rotation.setFromRotationMatrix(handMatrix); - } else { - if (handState.lastVisible) { - handObject3D.scale.set(0.0000001, 0.0000001, 0.0000001); - handState.lastVisible = false; - } } } }); diff --git a/src/components/in-world-hud.js b/src/components/in-world-hud.js index e69cfd7374eaa1ca1972b3cb00784d837dc987b8..aa7e8aae756207dc05a640f7d02f6c0f728b41ca 100644 --- a/src/components/in-world-hud.js +++ b/src/components/in-world-hud.js @@ -7,8 +7,18 @@ AFRAME.registerComponent("in-world-hud", { this.bg = this.el.querySelector(".bg"); this.mic = this.el.querySelector(".mic"); this.nametag = this.el.querySelector(".username"); + this.avatar = this.el.querySelector(".avatar"); this.nametag.object3DMap.text.material.depthTest = false; - this.data.raycaster.components.line.material.depthTest = false; + this.nametag.object3DMap.text.renderOrder = 1; + this.mic.object3DMap.mesh.renderOrder = 1; + this.avatar.object3DMap.mesh.renderOrder = 1; + + const line = this.data.raycaster.object3DMap.line; + line.renderOrder = 2; + line.material.depthTest = false; + // Set opacity to 0.99 to force the line to render in the + // transparent pass, so that it renders on top of the HUD + this.data.raycaster.setAttribute("line", "opacity", 0.99); const muted = this.el.sceneEl.is("muted"); this.mic.setAttribute("src", muted ? "#muted" : "#unmuted"); @@ -96,7 +106,7 @@ AFRAME.registerComponent("in-world-hud", { this.el.sceneEl.removeEventListener("micAudio", this.onAudioFrequencyChange); }, - tick: function(t, dt) { + tick: function() { if (!this.analyser) return; this.analyser.getByteFrequencyData(this.levels); diff --git a/src/components/mute-mic.js b/src/components/mute-mic.js index 272b40e3b41f5d4aa49d7107b6aa41785b8d5596..00729ba1904002a88729e21ed82f69dbb872dda9 100644 --- a/src/components/mute-mic.js +++ b/src/components/mute-mic.js @@ -1,6 +1,6 @@ const bindAllEvents = function(elements, events, f) { if (!elements || !elements.length) return; - for (var el of elements) { + for (const el of elements) { events.length && events.forEach(e => { el.addEventListener(e, f); @@ -9,7 +9,7 @@ const bindAllEvents = function(elements, events, f) { }; const unbindAllEvents = function(elements, events, f) { if (!elements || !elements.length) return; - for (var el of elements) { + for (const el of elements) { events.length && events.forEach(e => { el.removeEventListener(e, f); diff --git a/src/components/networked-counter.js b/src/components/networked-counter.js index c35dcbaa461fa5ca8627d7cf693455d4244ec9fe..39f76ddb7fb31482e86ddf4630f5d45d14c0d4e0 100644 --- a/src/components/networked-counter.js +++ b/src/components/networked-counter.js @@ -20,7 +20,7 @@ AFRAME.registerComponent("networked-counter", { item.el.removeEventListener(this.data.release_event, item.onReleaseHandler); } } - + for (const id in this.timeouts) { this._removeTimeout(id); } @@ -32,7 +32,7 @@ AFRAME.registerComponent("networked-counter", { } const id = NAF.utils.getNetworkId(networkedEl); - if (this.queue.hasOwnProperty(id)) { + if (id && this.queue.hasOwnProperty(id)) { return; } @@ -61,7 +61,7 @@ AFRAME.registerComponent("networked-counter", { deregister: function(networkedEl) { const id = NAF.utils.getNetworkId(networkedEl); - if (this.queue.hasOwnProperty(id)) { + if (id && this.queue.hasOwnProperty(id)) { const item = this.queue[id]; networkedEl.removeEventListener(this.data.grab_event, item.onGrabHandler); networkedEl.removeEventListener(this.data.release_event, item.onReleaseHandler); @@ -75,11 +75,11 @@ AFRAME.registerComponent("networked-counter", { } }, - _onGrabbed: function(id, e) { + _onGrabbed: function(id) { this._removeTimeout(id); }, - _onReleased: function(id, e) { + _onReleased: function(id) { this._removeTimeout(id); this._addTimeout(id); this.queue[id].ts = Date.now(); @@ -91,7 +91,6 @@ AFRAME.registerComponent("networked-counter", { ts = Number.MAX_VALUE; for (const id in this.queue) { if (this.queue.hasOwnProperty(id)) { - const expiration = this.queue[id].ts + this.data.ttl * 1000; if (this.queue[id].ts < ts && !this._isCurrentlyGrabbed(id)) { oldest = this.queue[id]; ts = this.queue[id].ts; diff --git a/src/components/networked-video-player.js b/src/components/networked-video-player.js index 51adcf2f7f2ddd6d0023c54442e03627ea952c70..189a43e393f359a5c2d217b5978132476f6407c6 100644 --- a/src/components/networked-video-player.js +++ b/src/components/networked-video-player.js @@ -25,7 +25,7 @@ AFRAME.registerComponent("networked-video-player", { if (ownerId !== NAF.clientId && rejectScreenShares) { // Toggle material visibility since object visibility is network-synced // TODO: There ought to be a better way to disable network syncs on a remote entity - this.el.setAttribute("material", {visible: false}); + this.el.setAttribute("material", { visible: false }); return; } diff --git a/src/components/offset-relative-to.js b/src/components/offset-relative-to.js index 923eae3e5673488e8eceb726b9e91f592285f546..f940b687284b34677c579e931557fc9671631f41 100644 --- a/src/components/offset-relative-to.js +++ b/src/components/offset-relative-to.js @@ -12,10 +12,7 @@ AFRAME.registerComponent("offset-relative-to", { }, init() { this.updateOffset(); - this.el.sceneEl.addEventListener( - this.data.on, - this.updateOffset.bind(this) - ); + this.el.sceneEl.addEventListener(this.data.on, this.updateOffset.bind(this)); }, updateOffset() { const offsetVector = new THREE.Vector3().copy(this.data.offset); diff --git a/src/components/player-info.js b/src/components/player-info.js index ac2fc6b55fcdaf0215e26327368c07dd67e88fcc..b9455352a104bd71aceb07ed4718a2999a4d08b6 100644 --- a/src/components/player-info.js +++ b/src/components/player-info.js @@ -12,7 +12,7 @@ AFRAME.registerComponent("player-info", { pause() { this.el.removeEventListener("model-loaded", this.applyProperties); }, - update(oldProps) { + update() { this.applyProperties(); }, applyProperties() { @@ -25,7 +25,7 @@ AFRAME.registerComponent("player-info", { const modelEl = this.el.querySelector(".model"); if (this.data.avatarSrc && modelEl) { - modelEl.setAttribute("src", this.data.avatarSrc); + modelEl.setAttribute("gltf-model-plus", "src", this.data.avatarSrc); } } }); diff --git a/src/components/super-cursor.js b/src/components/super-cursor.js index e498a1acc2ad9b99c9131134bd17560bee1af531..28589171658f9d06ed57b4e604d91a73032ea583 100644 --- a/src/components/super-cursor.js +++ b/src/components/super-cursor.js @@ -103,7 +103,7 @@ AFRAME.registerComponent("super-cursor", { } }, - _handleMouseDown: function(e) { + _handleMouseDown: function() { if (this.isInteractable) { const lookControls = this.data.camera.components["look-controls"]; lookControls.pause(); @@ -115,7 +115,7 @@ AFRAME.registerComponent("super-cursor", { this.mousePos.set(e.clientX / window.innerWidth * 2 - 1, -(e.clientY / window.innerHeight) * 2 + 1); }, - _handleMouseUp: function(e) { + _handleMouseUp: function() { const lookControls = this.data.camera.components["look-controls"]; lookControls.play(); this.data.cursor.emit("action_release", {}); @@ -125,13 +125,13 @@ AFRAME.registerComponent("super-cursor", { if (this.isGrabbing) this.currentDistanceMod += e.deltaY / 10; }, - _handleEnterVR: function(e) { + _handleEnterVR: function() { if (AFRAME.utils.device.checkHeadsetConnected() || AFRAME.utils.device.isMobile()) { this._disable(); } }, - _handleExitVR: function(e) { + _handleExitVR: function() { this._enable(); }, diff --git a/src/components/super-networked-interactable.js b/src/components/super-networked-interactable.js index 4aeafecaa460379852a0ff4e9eb0f0f0d25bf9f3..a5baed53dd2f3f5404a81a26150904353434e9e9 100644 --- a/src/components/super-networked-interactable.js +++ b/src/components/super-networked-interactable.js @@ -11,7 +11,7 @@ AFRAME.registerComponent("super-networked-interactable", { NAF.utils.getNetworkedEntity(this.el).then(networkedEl => { this.networkedEl = networkedEl; if (!NAF.utils.isMine(networkedEl)) { - this.el.setAttribute("body", {type: "dynamic", mass: 0}); + this.el.setAttribute("body", { type: "dynamic", mass: 0 }); } else { this.counter.register(networkedEl); } @@ -33,7 +33,7 @@ AFRAME.registerComponent("super-networked-interactable", { this.hand = e.detail.hand; if (this.networkedEl && !NAF.utils.isMine(this.networkedEl)) { if (NAF.utils.takeOwnership(this.networkedEl)) { - this.el.setAttribute("body", {mass: this.data.mass}); + this.el.setAttribute("body", { mass: this.data.mass }); this.counter.register(this.networkedEl); } else { this.el.emit("grab-end", { hand: this.hand }); @@ -42,8 +42,8 @@ AFRAME.registerComponent("super-networked-interactable", { } }, - _onOwnershipLost: function(e) { - this.el.setAttribute("body", {mass: 0}); + _onOwnershipLost: function() { + this.el.setAttribute("body", { mass: 0 }); this.el.emit("grab-end", { hand: this.hand }); this.hand = null; this.counter.deregister(this.el); diff --git a/src/components/super-spawner.js b/src/components/super-spawner.js index 72d24eff785b9eebed42c60e2e4feb21e659468a..bb6762a200eeacf53c29179f95554c7dc8fdab57 100644 --- a/src/components/super-spawner.js +++ b/src/components/super-spawner.js @@ -19,7 +19,7 @@ AFRAME.registerComponent("super-spawner", { }, remove: function() { - for (let entity of this.entities.keys()) { + for (const entity of this.entities.keys()) { const data = this.entities.get(entity); entity.removeEventListener("componentinitialized", data.componentinInitializedListener); entity.removeEventListener("bodyloaded", data.bodyLoadedListener); @@ -39,12 +39,12 @@ AFRAME.registerComponent("super-spawner", { this.entities.set(entity, { hand: hand, - componentInitialized: false, - bodyLoaded: false, - componentinInitializedListener: componentinInitializedListener, + componentInitialized: false, + bodyLoaded: false, + componentinInitializedListener: componentinInitializedListener, bodyLoadedListener: bodyLoadedListener }); - + entity.addEventListener("componentinitialized", componentinInitializedListener); entity.addEventListener("body-loaded", bodyLoadedListener); @@ -60,7 +60,7 @@ AFRAME.registerComponent("super-spawner", { } }, - _handleBodyLoaded: function(entity, e) { + _handleBodyLoaded: function(entity) { this.entities.get(entity).bodyLoaded = true; this._emitEvents.call(this, entity); }, diff --git a/src/components/virtual-gamepad-controls.js b/src/components/virtual-gamepad-controls.js index d399e38b29765280a677bb23289cdc3e4c2aff6b..d70219bf1e6374fefc6d6daaeb9e8e10bfc27fb8 100644 --- a/src/components/virtual-gamepad-controls.js +++ b/src/components/virtual-gamepad-controls.js @@ -1,10 +1,6 @@ import nipplejs from "nipplejs"; import styles from "./virtual-gamepad-controls.css"; -const THREE = AFRAME.THREE; -const DEGREES = Math.PI / 180; -const HALF_PI = Math.PI / 2; - AFRAME.registerComponent("virtual-gamepad-controls", { schema: {}, diff --git a/src/components/wasd-to-analog2d.js b/src/components/wasd-to-analog2d.js index a424cb893ab10c29575667f7c68539dd65e90689..35b68026bd121a35bb7f7c139d15423262475b86 100644 --- a/src/components/wasd-to-analog2d.js +++ b/src/components/wasd-to-analog2d.js @@ -44,7 +44,7 @@ AFRAME.registerComponent("wasd-to-analog2d", { this.keys[key] = down; }, - tick: function(t, dt) { + tick: function() { this.target = [0, 0]; for (const key in this.keys) { diff --git a/src/components/water.js b/src/components/water.js index 9152e548435ea8b184e9d1eb0f8cd4c2371217cc..b7f176131ea97f6f47fbbb31b27c59e80350b78b 100644 --- a/src/components/water.js +++ b/src/components/water.js @@ -17,24 +17,14 @@ function MobileWater(geometry, options) { options = options || {}; - const clipBias = options.clipBias !== undefined ? options.clipBias : 0.0; const time = options.time !== undefined ? options.time : 0.0; - const normalSampler = - options.waterNormals !== undefined ? options.waterNormals : null; + const normalSampler = options.waterNormals !== undefined ? options.waterNormals : null; const sunDirection = - options.sunDirection !== undefined - ? options.sunDirection - : new THREE.Vector3(0.70707, 0.70707, 0.0); - const sunColor = new THREE.Color( - options.sunColor !== undefined ? options.sunColor : 0xffffff - ); - const waterColor = new THREE.Color( - options.waterColor !== undefined ? options.waterColor : 0x7f7f7f - ); - const eye = - options.eye !== undefined ? options.eye : new THREE.Vector3(0, 0, 0); - const distortionScale = - options.distortionScale !== undefined ? options.distortionScale : 20.0; + options.sunDirection !== undefined ? options.sunDirection : new THREE.Vector3(0.70707, 0.70707, 0.0); + const sunColor = new THREE.Color(options.sunColor !== undefined ? options.sunColor : 0xffffff); + const waterColor = new THREE.Color(options.waterColor !== undefined ? options.waterColor : 0x7f7f7f); + const eye = options.eye !== undefined ? options.eye : new THREE.Vector3(0, 0, 0); + const distortionScale = options.distortionScale !== undefined ? options.distortionScale : 20.0; const side = options.side !== undefined ? options.side : THREE.FrontSide; const fog = options.fog !== undefined ? options.fog : false; diff --git a/src/elements/a-progressive-asset.js b/src/elements/a-progressive-asset.js deleted file mode 100644 index 8bf922aa17b10ce6eec09baf413e93dcf6d871f1..0000000000000000000000000000000000000000 --- a/src/elements/a-progressive-asset.js +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Modified version of a-asset-item that adds high-src and low-src options - * Extracted from https://github.com/aframevr/aframe/blob/master/src/core/a-assets.js - */ - -AFRAME.registerElement("a-progressive-asset", { - prototype: Object.create(AFRAME.ANode.prototype, { - createdCallback: { - value() { - this.data = null; - this.isAssetItem = true; - } - }, - - attachedCallback: { - value() { - if (!this.parentNode.fileLoader) { - throw new Error("a-progressive-asset must be the child of an a-assets element."); - } - - this.fileLoader = this.parentNode.fileLoader; - - const self = this; - const fallbackSrc = this.getAttribute("src"); - const highSrc = this.getAttribute("high-src"); - const lowSrc = this.getAttribute("low-src"); - - let src = fallbackSrc; - - if (window.APP.quality === "high") { - src = highSrc; - } else if (window.APP.quality === "low") { - src = lowSrc; - } - - this.fileLoader.setResponseType(this.getAttribute("response-type") || inferResponseType(src)); - this.fileLoader.load( - src, - function handleOnLoad(response) { - self.data = response; - /* - Workaround for a Chrome bug. If another XHR is sent to the same url before the - previous one closes, the second request never finishes. - setTimeout finishes the first request and lets the logic triggered by load open - subsequent requests. - setTimeout can be removed once the fix for the bug below ships: - https://bugs.chromium.org/p/chromium/issues/detail?id=633696&q=component%3ABlink%3ENetwork%3EXHR%20&colspec=ID%20Pri%20M%20Stars%20ReleaseBlock%20Component%20Status%20Owner%20Summary%20OS%20Modified - */ - setTimeout(function load() { - AFRAME.ANode.prototype.load.call(self); - }); - }, - function handleOnProgress(xhr) { - self.emit("progress", { - loadedBytes: xhr.loaded, - totalBytes: xhr.total, - xhr: xhr - }); - }, - function handleOnError(xhr) { - self.emit("error", { xhr: xhr }); - } - ); - } - } - }) -}); diff --git a/src/gltf-component-mappings.js b/src/gltf-component-mappings.js index e4cf086733ad1e0dc547abafe3fb37ed025a5608..7c6b6a8d8774eeeda0de6f0fb074848a5e78be51 100644 --- a/src/gltf-component-mappings.js +++ b/src/gltf-component-mappings.js @@ -1,4 +1,4 @@ -import "./elements/a-gltf-entity"; +import "./components/gltf-model-plus"; -AFRAME.AGLTFEntity.registerComponent("scale-audio-feedback", "scale-audio-feedback"); -AFRAME.AGLTFEntity.registerComponent("loop-animation", "loop-animation"); +AFRAME.GLTFModelPlus.registerComponent("scale-audio-feedback", "scale-audio-feedback"); +AFRAME.GLTFModelPlus.registerComponent("loop-animation", "loop-animation"); diff --git a/src/room.html b/src/hub.html similarity index 66% rename from src/room.html rename to src/hub.html index 8c2a6bf83b87884125e16c9bdc122c250344bfcf..cd38991c4f82a587f455afcedd77f5db711d7351 100644 --- a/src/room.html +++ b/src/hub.html @@ -3,8 +3,9 @@ <head> <meta charset="utf-8"> - <title>Mozilla Mixed Reality Social Client</title> + <title>moz://a duck</title> + <meta name="viewport" content="width=device-width, initial-scale=1"> <meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="<%= ORIGIN_TRIAL_EXPIRES %>" content="<%= ORIGIN_TRIAL_TOKEN %>"> <% if(NODE_ENV === "production") { %> <script src="https://cdn.rawgit.com/brianpeiris/aframe/845825ae694449524c185c44a314d361eead4680/dist/aframe-master.min.js"></script> @@ -13,12 +14,14 @@ <% } %> </head> -<body> +<body data-html-prefix="<%= HTML_PREFIX %>"> <audio id="test-tone" src="./assets/sfx/tone.ogg"></audio> <a-scene - physics + networked-scene="adapter: janus; audio: true; debug: true; connectOnLoad: false;" + physics="debug:true" mute-mic="eventSrc: a-scene; toggleEvents: action_mute" + personal-space-bubble="debug: false;" app-mode-input-mappings="modes: default, hud; actionSets: default, hud;" > @@ -28,77 +31,20 @@ <img id="muted" src="./assets/hud/muted.png" > <img id="avatar" src="./assets/hud/avatar.jpg" > - <a-progressive-asset - id="botdefault" - response-type="arraybuffer" - src="./assets/avatars/BotDefault_Avatar_Unlit.glb" - high-src="./assets/avatars/BotDefault_Avatar.glb" - low-src="./assets/avatars/BotDefault_Avatar_Unlit.glb" - ></a-progressive-asset> - <a-progressive-asset - id="botbobo" - response-type="arraybuffer" - src="./assets/avatars/BotBobo_Avatar_Unlit.glb" - high-src="./assets/avatars/BotBobo_Avatar.glb" - low-src="./assets/avatars/BotBobo_Avatar_Unlit.glb" - ></a-progressive-asset> - <a-progressive-asset - id="botdom" - response-type="arraybuffer" - src="./assets/avatars/BotDom_Avatar_Unlit.glb" - high-src="./assets/avatars/BotDom_Avatar.glb" - low-src="./assets/avatars/BotDom_Avatar_Unlit.glb" - ></a-progressive-asset> - <a-progressive-asset - id="botgreg" - response-type="arraybuffer" - src="./assets/avatars/BotGreg_Avatar_Unlit.glb" - high-src="./assets/avatars/BotGreg_Avatar.glb" - low-src="./assets/avatars/BotGreg_Avatar_Unlit.glb" - ></a-progressive-asset> - <a-progressive-asset - id="botguest" - response-type="arraybuffer" - src="./assets/avatars/BotGuest_Avatar_Unlit.glb" - high-src="./assets/avatars/BotGuest_Avatar.glb" - low-src="./assets/avatars/BotGuest_Avatar_Unlit.glb" - ></a-progressive-asset> - <a-progressive-asset - id="botjim" - response-type="arraybuffer" - src="./assets/avatars/BotJim_Avatar_Unlit.glb" - high-src="./assets/avatars/BotJim_Avatar.glb" - low-src="./assets/avatars/BotJim_Avatar_Unlit.glb" - ></a-progressive-asset> - <a-progressive-asset - id="botpinky" - response-type="arraybuffer" - src="./assets/avatars/BotPinky_Avatar_Unlit.glb" - high-src="./assets/avatars/BotPinky_Avatar.glb" - low-src="./assets/avatars/BotPinky_Avatar_Unlit.glb" - ></a-progressive-asset> - <a-progressive-asset - id="botrobert" - response-type="arraybuffer" - src="./assets/avatars/BotRobert_Avatar_Unlit.glb" - high-src="./assets/avatars/BotRobert_Avatar.glb" - low-src="./assets/avatars/BotRobert_Avatar_Unlit.glb" - ></a-progressive-asset> - <a-progressive-asset - id="botwoody" - response-type="arraybuffer" - src="./assets/avatars/BotWoody_Avatar_Unlit.glb" - high-src="./assets/avatars/BotWoody_Avatar.glb" - low-src="./assets/avatars/BotWoody_Avatar_Unlit.glb" - ></a-progressive-asset> + <a-asset-item id="botdefault" response-type="arraybuffer" src="https://asset-bundles-dev.reticulum.io/bots/BotDefault_Avatar-9f71f8ff22.gltf"></a-asset-item> + <a-asset-item id="botbobo" response-type="arraybuffer" src="https://asset-bundles-dev.reticulum.io/bots/BotBobo_Avatar-f9740a010b.gltf"></a-asset-item> + <a-asset-item id="botdom" response-type="arraybuffer" src="https://asset-bundles-dev.reticulum.io/bots/BotDom_Avatar-6aa1b5d781.gltf"></a-asset-item> + <a-asset-item id="botgreg" response-type="arraybuffer" src="https://asset-bundles-dev.reticulum.io/bots/BotGreg_Avatar-98d39797bb.gltf"></a-asset-item> + <a-asset-item id="botguest" response-type="arraybuffer" src="https://asset-bundles-dev.reticulum.io/bots/BotGuest_Avatar-78cd857332.gltf"></a-asset-item> + <a-asset-item id="botjim" response-type="arraybuffer" src="https://asset-bundles-dev.reticulum.io/bots/BotJim_Avatar-d28005a687.gltf"></a-asset-item> + <a-asset-item id="botkev" response-type="arraybuffer" src="https://asset-bundles-dev.reticulum.io/bots/BotKev_Avatar-a95787bb51.gltf"></a-asset-item> + <a-asset-item id="botpinky" response-type="arraybuffer" src="https://asset-bundles-dev.reticulum.io/bots/BotPinky_Avatar-b0b93f8675.gltf"></a-asset-item> + <a-asset-item id="botrobert" response-type="arraybuffer" src="https://asset-bundles-dev.reticulum.io/bots/BotRobert_Avatar-e9554880f3.gltf"></a-asset-item> + <a-asset-item id="botwoody" response-type="arraybuffer" src="https://asset-bundles-dev.reticulum.io/bots/BotWoody_Avatar-0140485a23.gltf"></a-asset-item> <a-asset-item id="watch-model" response-type="arraybuffer" src="./assets/hud/watch.glb"></a-asset-item> - - <a-asset-item id="meeting-space1-mesh" response-type="arraybuffer" src="./assets/environments/MeetingSpace1_mesh.glb"></a-asset-item> - <a-asset-item id="outdoor-facade-mesh" response-type="arraybuffer" src="./assets/environments/OutdoorFacade_mesh.glb"></a-asset-item> - <a-asset-item id="floor-nav-mesh" response-type="arraybuffer" src="./assets/environments/FloorNav_mesh.glb"></a-asset-item> - <a-asset-item id="cliff-vista-mesh" response-type="arraybuffer" src="./assets/environments/CliffVista_mesh.glb"></a-asset-item> <a-asset-item id="interactable-duck" response-type="arraybuffer" src="./assets/interactables/duck/DuckyMesh.glb"></a-asset-item> + <a-asset-item id="navmesh" response-type="arraybuffer" src="./assets/environments/cliff_meeting_space/FloorNav_mesh.glb"></a-asset-item> <img id="water-normal-map" src="./assets/waternormals.jpg"></a-asset-item> @@ -116,9 +62,9 @@ <a-entity class="right-controller"></a-entity> - <a-gltf-entity class="model" inflate="true"> + <a-entity class="model" gltf-model-plus="inflate: true"> <template data-selector=".RootScene"> - <a-entity ik-controller animation-mixer></a-entity> + <a-entity ik-controller animation-mixer space-invader-mesh="meshSelector: .Bot_Skinned"></a-entity> </template> <template data-selector=".Neck"> @@ -133,29 +79,34 @@ </a-entity> </template> + <template data-selector=".Chest"> + <a-entity personal-space-invader="radius: 0.2; useMaterial: true;" bone-visibility></a-entity> + </template> + <template data-selector=".Head"> <a-entity networked-audio-source networked-audio-analyser - personal-space-invader + personal-space-invader="radius: 0.15; useMaterial: true;" + bone-visibility > </a-entity> </template> <template data-selector=".LeftHand"> - <a-entity personal-space-invader ></a-entity> + <a-entity personal-space-invader="radius: 0.1" bone-visibility></a-entity> </template> <template data-selector=".RightHand"> - <a-entity personal-space-invader ></a-entity> + <a-entity personal-space-invader="radius: 0.1" bone-visibility></a-entity> </template> - </a-gltf-entity> + </a-entity> </a-entity> </template> <template id="interactable-template"> <a-entity - gltf-model="#interactable-duck" + gltf-model-plus="src: #interactable-duck" scale="2 2 2" class="interactable" super-networked-interactable="counter: #counter; mass: 5;" @@ -180,8 +131,8 @@ <!-- Interactables --> <a-entity id="counter" networked-counter="max: 3; ttl: 120"></a-entity> - <a-entity - gltf-model="#interactable-duck" + <a-entity + gltf-model-plus="src: #interactable-duck" scale="2 2 2" class="interactable" super-spawner="template: #interactable-template;" @@ -236,7 +187,7 @@ class="camera" camera position="0 1.6 0" - personal-space-bubble + personal-space-bubble="radius: 0.4" look-controls ></a-entity> @@ -245,9 +196,10 @@ class="left-controller" hand-controls2="left" tracked-controls - teleport-controls="cameraRig: #player-rig; + teleport-controls=" + cameraRig: #player-rig; teleportOrigin: #player-camera; - button: action_teleport_; + button: action_teleport_; collisionEntities: #floor-nav" app-mode-toggle-playing__teleport-controls="mode: hud; invert: true;" haptic-feedback @@ -260,9 +212,13 @@ class="right-controller" hand-controls2="right" tracked-controls - teleport-controls="cameraRig: #player-rig; teleportOrigin: #player-camera; button: action_teleport_; collisionEntities: #floor-nav" + teleport-controls=" + cameraRig: #player-rig; + teleportOrigin: #player-camera; + button: action_teleport_; + collisionEntities: #floor-nav" haptic-feedback - raycaster="objects:.hud; showLine: true;" + raycaster="objects:.hud; showLine: true; far: 2;" cursor="fuse: false; downEvents: action_ui_select_down; upEvents: action_ui_select_up;" app-mode-toggle-playing__teleport-controls="mode: hud; invert: true;" @@ -271,7 +227,7 @@ app-mode-toggle-attribute__line="mode: hud; property: visible;" ></a-entity> - <a-gltf-entity class="model" inflate="true"> + <a-entity gltf-model-plus="inflate: true;" class="model"> <template data-selector=".RootScene"> <a-entity ik-controller @@ -287,19 +243,19 @@ </template> <template data-selector=".Head"> - <a-entity visible="false"></a-entity> + <a-entity visible="false" bone-visibility></a-entity> </template> <template data-selector=".LeftHand"> - <a-entity> - <a-gltf-entity + <a-entity bone-visibility> + <a-entity id="watch" - src="#watch-model" + gltf-model-plus="src: #watch-model" bone-mute-state-indicator scale="1.5 1.5 1.5" rotation="0 -90 90" position="0 -0.04 0" - ></a-gltf-entity> + ></a--entity> <a-entity event-repeater="events: action_grab, action_release; eventSource: #player-left-controller" static-body="shape: sphere; sphereRadius: 0.02" @@ -310,7 +266,7 @@ </template> <template data-selector=".RightHand"> - <a-entity> + <a-entity bone-visibility> <a-entity event-repeater="events: action_grab, action_release; eventSource: #player-right-controller" static-body="shape: sphere; sphereRadius: 0.02" @@ -320,7 +276,7 @@ </a-entity> </template> - </a-gltf-entity> + </a-entity> </a-entity> <!-- Lights --> @@ -331,35 +287,15 @@ ></a-entity> <!-- Environment --> - <a-gltf-entity - id="meeting-space" - src="#meeting-space1-mesh" - position="0 0 0" - ></a-gltf-entity> - - <a-gltf-entity - id="outdoor-facade" - src="#outdoor-facade-mesh" - position="0 0 0" - xr="ar: false" - ></a-gltf-entity> + <a-entity id="environment-root" position="0 0 0"></a-entity> - <a-gltf-entity - id="floor-nav" + <a-entity + id="nav-mesh" + gltf-model-plus="src: #navmesh" nav-mesh - src="#floor-nav-mesh" visible="false" - position="0 0 0" xr="ar: false" - ></a-gltf-entity> - - <a-gltf-entity - id="cliff-vista" - src="#cliff-vista-mesh" - layers="reflection:true" - position="0 0 0" - xr="ar: false" - ></a-gltf-entity> + ></a-entity> <a-entity id="skybox" id="skybox" diff --git a/src/room.js b/src/hub.js similarity index 79% rename from src/room.js rename to src/hub.js index fd0a7a74ac4cec216da04e268b0b0d9268327a12..bb8ae791192f9490f2e785249056f5103140ed26 100644 --- a/src/room.js +++ b/src/hub.js @@ -1,4 +1,4 @@ -import "./assets/stylesheets/room.scss"; +import "./assets/stylesheets/hub.scss"; import queryString from "query-string"; import { patchWebGLRenderingContext } from "./utils/webgl"; @@ -23,6 +23,7 @@ import "./components/wasd-to-analog2d"; //Might be a behaviour or activator in t import "./components/mute-mic"; import "./components/audio-feedback"; import "./components/bone-mute-state-indicator"; +import "./components/bone-visibility"; import "./components/in-world-hud"; import "./components/virtual-gamepad-controls"; import "./components/ik-controller"; @@ -41,6 +42,8 @@ import "./components/player-info"; import "./components/debug"; import "./components/animation-mixer"; import "./components/loop-animation"; +import "./components/gltf-model-plus"; +import "./components/gltf-bundle"; import ReactDOM from "react-dom"; import React from "react"; @@ -49,8 +52,6 @@ import UIRoot from "./react-components/ui-root"; import "./systems/personal-space-bubble"; import "./systems/app-mode"; -import "./elements/a-gltf-entity"; - import "./gltf-component-mappings"; import { App } from "./App"; @@ -66,8 +67,6 @@ if (qs.quality) { window.APP.quality = isMobile ? "low" : "high"; } -import "./elements/a-progressive-asset"; - import "aframe-physics-system"; import "aframe-physics-extras"; import "aframe-extras/src/pathfinding"; @@ -103,6 +102,7 @@ AFRAME.registerInputMappings(inputConfig, true); const store = new Store(); const concurrentLoadDetector = new ConcurrentLoadDetector(); + concurrentLoadDetector.start(); // Always layer in any new default profile bits @@ -118,16 +118,15 @@ function applyProfileFromStore(playerRig) { const displayName = store.state.profile.display_name; playerRig.setAttribute("player-info", { displayName, - avatarSrc: '#' + (store.state.profile.avatar_id || "botdefault") + avatarSrc: "#" + (store.state.profile.avatar_id || "botdefault") }); document.querySelector("a-scene").emit("username-changed", { username: displayName }); } -async function enterScene(mediaStream, enterInVR) { +async function enterScene(mediaStream, enterInVR, janusRoomId) { const scene = document.querySelector("a-scene"); const playerRig = document.querySelector("#player-rig"); document.querySelector("a-scene canvas").classList.remove("blurred"); - registerNetworkSchemas(); if (enterInVR) { scene.enterVR(); @@ -135,12 +134,10 @@ async function enterScene(mediaStream, enterInVR) { AFRAME.registerInputActions(inGameActions, "default"); + document.querySelector("#player-camera").setAttribute("look-controls"); + scene.setAttribute("networked-scene", { - adapter: "janus", - audio: true, - debug: true, - connectOnLoad: false, - room: qs.room && !isNaN(parseInt(qs.room, 10)) ? parseInt(qs.room, 10) : 1, + room: janusRoomId, serverURL: process.env.JANUS_SERVER }); @@ -174,8 +171,7 @@ async function enterScene(mediaStream, enterInVR) { for (const track of videoTracks) { mediaStream.addTrack(track); } - } - else { + } else { for (const track of mediaStream.getVideoTracks()) { mediaStream.removeTrack(track); } @@ -215,11 +211,13 @@ async function enterScene(mediaStream, enterInVR) { function onConnect() {} function mountUI(scene) { - const qs = queryString.parse(location.search); const disableAutoExitOnConcurrentLoad = qsTruthy("allow_multi"); const forcedVREntryType = qs.vr_entry_type || null; const enableScreenSharing = qsTruthy("enable_screen_sharing"); + const htmlPrefix = document.body.dataset.htmlPrefix || ""; + // TODO: Refactor to avoid using return value + /* eslint-disable react/no-render-return-value */ const uiRoot = ReactDOM.render( <UIRoot {...{ @@ -230,23 +228,53 @@ function mountUI(scene) { disableAutoExitOnConcurrentLoad, forcedVREntryType, enableScreenSharing, - store + store, + htmlPrefix }} />, document.getElementById("ui-root") ); + /* eslint-enable react/no-render-return-value */ - getAvailableVREntryTypes().then(availableVREntryTypes => { - uiRoot.setState({ availableVREntryTypes }); - uiRoot.handleForcedVREntryType(); - }); + return uiRoot; } -const onReady = () => { +const onReady = async () => { const scene = document.querySelector("a-scene"); document.querySelector("a-scene canvas").classList.add("blurred"); window.APP.scene = scene; - mountUI(scene); + + registerNetworkSchemas(); + + const uiRoot = mountUI(scene); + + getAvailableVREntryTypes().then(availableVREntryTypes => { + uiRoot.setState({ availableVREntryTypes }); + uiRoot.handleForcedVREntryType(); + }); + + const environmentRoot = document.querySelector("#environment-root"); + + const initialEnvironmentEl = document.createElement("a-entity"); + initialEnvironmentEl.addEventListener("bundleloaded", () => uiRoot.setState({ initialEnvironmentLoaded: true })); + environmentRoot.appendChild(initialEnvironmentEl); + + if (qs.room) { + // If ?room is set, this is `yarn start`, so just use a default environment and query string room. + uiRoot.setState({ janusRoomId: qs.room && !isNaN(parseInt(qs.room)) ? parseInt(qs.room) : 1 }); + initialEnvironmentEl.setAttribute("gltf-bundle", "src: /assets/environments/cliff_meeting_space/bundle.json"); + return; + } + + const hubId = document.location.pathname.substring(1).split("/")[0]; + console.log(`Hub ID: ${hubId}`); + const res = await fetch(`/api/v1/hubs/${hubId}`); + const data = await res.json(); + const hub = data.hubs[0]; + const defaultSpaceTopic = hub.topics[0]; + const gltfBundleUrl = defaultSpaceTopic.assets.find(a => a.asset_type === "gltf_bundle").src; + uiRoot.setState({ janusRoomId: defaultSpaceTopic.janus_room_id }); + initialEnvironmentEl.setAttribute("gltf-bundle", `src: ${gltfBundleUrl}`); }; document.addEventListener("DOMContentLoaded", onReady); diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000000000000000000000000000000000000..6e0d03c001627862dcba3b8e3db3e991afc29a4e --- /dev/null +++ b/src/index.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> + +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>moz://a duck</title> + <link href="https://fonts.googleapis.com/css?family=Zilla+Slab:300,300i,400,400i,700" rel="stylesheet"> +</head> + +<body> + <div id="home-root" class="home-root"></div> +</body> + +</html> diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000000000000000000000000000000000000..198ee8d47378bd740fc9a2891cc6945447442b2a --- /dev/null +++ b/src/index.js @@ -0,0 +1,8 @@ +import "./assets/stylesheets/index.scss"; +import React from "react"; +import ReactDOM from "react-dom"; +import HomeRoot from "./react-components/home-root"; +import registerTelemetry from "./telemetry"; + +registerTelemetry(); +ReactDOM.render(<HomeRoot />, document.getElementById("home-root")); diff --git a/src/input-mappings.js b/src/input-mappings.js index a5338250079c08c94d79edb84457ff4e3b4e7b65..035110dd677efa748c280ae0d6b8f14fde1ae6f4 100644 --- a/src/input-mappings.js +++ b/src/input-mappings.js @@ -31,14 +31,19 @@ const config = { mappings: { default: { "vive-controls": { - menudown: "action_mute", + menudown: ["action_mute", "thumb_down"], + menuup: "thumb_up", "trackpad.pressedmove": { left: "move" }, trackpad_dpad4_pressed_west_down: { right: "snap_rotate_left" }, trackpad_dpad4_pressed_east_down: { right: "snap_rotate_right" }, trackpad_dpad4_pressed_center_down: { right: "action_teleport_down" }, trackpadup: { right: "action_teleport_up" }, - gripdown: "action_grab", - gripup: "action_release" + gripdown: ["action_grab", "middle_ring_pinky_down", "index_down"], + gripup: ["action_release", "middle_ring_pinky_up", "index_up"], + trackpadtouchstart: "thumb_down", + trackpadtouchend: "thumb_up", + triggerdown: "index_down", + triggerup: "index_up" }, "oculus-touch-controls": { joystick_dpad4_west: { diff --git a/src/lobby.css b/src/lobby.css deleted file mode 100644 index 539c8ab7976b745447203a281529e1aa6c80ef00..0000000000000000000000000000000000000000 --- a/src/lobby.css +++ /dev/null @@ -1,58 +0,0 @@ -* { - box-sizing: border-box; -} - -body { - margin: 0; - padding: 0; - background: black url(./assets/background.jpg) no-repeat left center; - background-size: cover; -} - -.overlay { - display: flex; - background: rgba(0, 0, 0, 0.4); - width: 100vw; - height: 100vh; - align-items: center; - padding: 100px 50px; -} - -.panel { - height: 100%; - display: flex; -} - -.scroll { - flex: 1; - overflow-y: auto; -} - -.mdl-list { - margin: 0; -} - -.room-item { - padding: 0; -} - -.room-item a { - width: 100%; - height: 100%; - padding: 16px; - color: inherit; - text-decoration: none; -} - -.room-item a:hover { - background: #eaeaea; -} - -@media only screen and (max-width: 320px) { - .overlay { - padding: 0; - } - .panel { - width: 100%; - } -} diff --git a/src/lobby.html b/src/lobby.html deleted file mode 100644 index 3fd52acf4da53f06c6fb91a19a62b3f89339f01c..0000000000000000000000000000000000000000 --- a/src/lobby.html +++ /dev/null @@ -1,14 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <title>Mozilla Mixed Reality Social Client Lobby</title> - <meta charset="utf-8"> - <meta http-equiv="X-UA-Compatible" content="IE=edge"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - - <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"> - </head> - <body> - <div id="root"></div> - </body> -</html> diff --git a/src/lobby.js b/src/lobby.js deleted file mode 100644 index c3c9a6baed6d3da99508d08a8dd3b37b148cfd58..0000000000000000000000000000000000000000 --- a/src/lobby.js +++ /dev/null @@ -1,116 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom"; -import { JanusSession, JanusPluginHandle } from "minijanus"; - -import "material-design-lite"; -import "material-design-lite/material.css"; -import "./lobby.css"; -import "webrtc-adapter"; - -import registerTelemetry from "./telemetry"; - -const publicRooms = [1, 2, 3, 4, 5]; - -registerTelemetry(); - -class Lobby extends React.Component { - constructor() { - super(); - this.state = { rooms: [] }; - - this.onWebsocketOpen = this.onWebsocketOpen.bind(this); - this.onWebsocketMessage = this.onWebsocketMessage.bind(this); - this.updateRooms = this.updateRooms.bind(this); - } - - componentDidMount() { - this.ws = new WebSocket(process.env.JANUS_SERVER, "janus-protocol"); - this.session = new JanusSession(this.ws.send.bind(this.ws)); - this.ws.addEventListener("open", this.onWebsocketOpen); - this.ws.addEventListener("message", this.onWebsocketMessage); - } - - componentWillUnmount() { - clearInterval(this.pollRoomsIntervalHandle); - this.ws.removeEventListener("open", this.onWebsocketOpen); - this.ws.removeEventListener("message", this.onWebsocketMessage); - } - - onWebsocketOpen() { - this.session - .create() - .then(() => { - this.handle = new JanusPluginHandle(this.session); - return this.handle.attach("janus.plugin.sfu").then(this.updateRooms); - }) - .then(() => { - this.pollRoomsIntervalHandle = setInterval(this.updateRooms, 10000); - }); - } - - updateRooms() { - return this.fetchRooms().then(rooms => this.setState({ rooms })); - } - - fetchRooms() { - return this.handle.sendMessage({ kind: "listusers" }).then(signal => { - const usersByRoom = signal.plugindata.data.response.users; - return publicRooms.map(id => ({ - id, - limit: 12, - users: usersByRoom[id] || [] - })); - }); - } - - onWebsocketMessage(event) { - this.session.receive(JSON.parse(event.data)); - } - - render() { - return ( - <div className="overlay"> - <RoomList rooms={this.state.rooms} /> - </div> - ); - } -} - -const RoomListItem = ({ room }) => { - return ( - <li className="room-item mdl-list__item mdl-list__item--two-line"> - <a href={`room.html?room=${room.id}`}> - <span className="mdl-list__item-primary-content"> - <i className="material-icons mdl-list__item-icon">room</i> - <span>Meeting room {room.id}</span> - <span className="mdl-list__item-sub-title"> - {room.users.length}/{room.limit} - </span> - </span> - </a> - </li> - ); -}; - -const RoomList = ({ rooms }) => { - const roomId = - publicRooms.length + 1 + Math.floor(Math.random() * (Number.MAX_SAFE_INTEGER - publicRooms.length + 1)); - - return ( - <div className="mdl-card mdl-shadow--2dp panel"> - <ul className="mdl-list scroll">{rooms.map(room => <RoomListItem key={room.id} room={room} />)}</ul> - <ul className="mdl-list"> - <li className="mdl-list__item room-item"> - <a href={`room.html?room=${roomId}`}> - <span className="mdl-list__item-primary-content"> - <i className="material-icons mdl-list__item-icon">add</i> - New Meeting Room - </span> - </a> - </li> - </ul> - </div> - ); -}; - -ReactDOM.render(<Lobby />, document.getElementById("root")); diff --git a/src/onboarding.css b/src/onboarding.css deleted file mode 100644 index c3332df4090424929d76fa27159646a43972ee3e..0000000000000000000000000000000000000000 --- a/src/onboarding.css +++ /dev/null @@ -1,15 +0,0 @@ -* { - box-sizing: border-box; -} - -body { - margin: 0; - padding: 0; -} - -:local(.onboarding) { - display: flex; - flex: 1; - margin: 20px; - flex-direction: column; -} diff --git a/src/onboarding.html b/src/onboarding.html deleted file mode 100644 index cda90ad3c0623819574f4827d25af2447ac2b5d1..0000000000000000000000000000000000000000 --- a/src/onboarding.html +++ /dev/null @@ -1,12 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <title>Mozilla Mixed Reality Social Client Lobby</title> - <meta charset="utf-8"> - <meta http-equiv="X-UA-Compatible" content="IE=edge"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - </head> - <body> - <div id="root"></div> - </body> -</html> diff --git a/src/onboarding.js b/src/onboarding.js deleted file mode 100644 index a3d25c27b1e1b6fbbfdcd0cc432b345ca2e79a75..0000000000000000000000000000000000000000 --- a/src/onboarding.js +++ /dev/null @@ -1,51 +0,0 @@ -import ReactDOM from "react-dom"; -import React from "react"; -import { HashRouter as Router, Route, Link } from "react-router-dom"; -import styles from "./onboarding.css"; - -const Onboarding = () => ( - <Router basename=""> - <div className={styles.onboarding}> - <Link to="/">Start Over</Link> - - <hr /> - - <Route exact path="/" component={Landing} /> - <Route path="/step1" component={Step1} /> - <Route path="/step2" component={Step2} /> - <Route path="/step3" component={Step3} /> - </div> - </Router> -); - -const Landing = () => ( - <div> - <h2>Landing</h2> - <Link to="/step1">Get Started</Link> - </div> -); - -const Step1 = () => ( - <div> - <h2>Step 1</h2> - <Link to="/step2">Next</Link> - </div> -); - -const Step2 = () => ( - <div> - <h2>Step 2</h2> - <Link to="/step1">Back</Link> - <Link to="/step3">Next</Link> - </div> -); - -const Step3 = () => ( - <div> - <h2>Step 3</h2> - <Link to="/step2">Back</Link> - <a href="/room.html?room=1">Create Room</a> - </div> -); - -ReactDOM.render(<Onboarding />, document.getElementById("root")); diff --git a/src/react-components/2d-hud.js b/src/react-components/2d-hud.js index fda3ae06ad0ce63b7b773d5a69f4141b52389c9f..8d9223a072c97c670210e67e48434cfc58543d49 100644 --- a/src/react-components/2d-hud.js +++ b/src/react-components/2d-hud.js @@ -1,8 +1,8 @@ -import React, { Component } from "react"; +import React from "react"; import PropTypes from "prop-types"; import cx from "classnames"; -import styles from "./2d-hud.css"; +import styles from "../assets/stylesheets/2d-hud.css"; const TwoDHUD = ({ name, muted, onToggleMute }) => ( <div className={styles.container}> diff --git a/src/react-components/auto-exit-warning.js b/src/react-components/auto-exit-warning.js index 8663dcca2277d42456511adb212238d3bcdb936f..d8691a85182c7f8bbd3ee9a7472e428df9aa2d3b 100644 --- a/src/react-components/auto-exit-warning.js +++ b/src/react-components/auto-exit-warning.js @@ -1,4 +1,4 @@ -import React, { Component } from "react"; +import React from "react"; import { FormattedMessage } from "react-intl"; import PropTypes from "prop-types"; diff --git a/src/react-components/avatar-selector.js b/src/react-components/avatar-selector.js index 7cf71b7c5e130c2bbd16a54e5b247f3a1cea74d5..2256845bb5538970c64f83047decb8842a615e7d 100644 --- a/src/react-components/avatar-selector.js +++ b/src/react-components/avatar-selector.js @@ -1,124 +1,107 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { injectIntl, FormattedMessage } from 'react-intl'; -import FontAwesomeIcon from '@fortawesome/react-fontawesome'; -import faAngleLeft from '@fortawesome/fontawesome-free-solid/faAngleLeft'; -import faAngleRight from '@fortawesome/fontawesome-free-solid/faAngleRight'; -import meetingSpace from '../assets/environments/MeetingSpace1_mesh.glb'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { injectIntl, FormattedMessage } from "react-intl"; +import FontAwesomeIcon from "@fortawesome/react-fontawesome"; +import faAngleLeft from "@fortawesome/fontawesome-free-solid/faAngleLeft"; +import faAngleRight from "@fortawesome/fontawesome-free-solid/faAngleRight"; +import meetingSpace from "../assets/environments/cliff_meeting_space/MeetingSpace1_mesh.glb"; class AvatarSelector extends Component { static propTypes = { avatars: PropTypes.array, avatarId: PropTypes.string, - onChange: PropTypes.func, - } + onChange: PropTypes.func + }; - getAvatarIndex = (direction=0) => { + getAvatarIndex = (direction = 0) => { const currAvatarIndex = this.props.avatars.findIndex(avatar => avatar.id === this.props.avatarId); const numAvatars = this.props.avatars.length; return ((currAvatarIndex + direction) % numAvatars + numAvatars) % numAvatars; - } - nextAvatarIndex = () => this.getAvatarIndex(1) - previousAvatarIndex = () => this.getAvatarIndex(-1) + }; + nextAvatarIndex = () => this.getAvatarIndex(1); + previousAvatarIndex = () => this.getAvatarIndex(-1); emitChangeToNext = () => { const nextAvatarId = this.props.avatars[this.nextAvatarIndex()].id; this.props.onChange(nextAvatarId); - } + }; emitChangeToPrevious = () => { const previousAvatarId = this.props.avatars[this.previousAvatarIndex()].id; this.props.onChange(previousAvatarId); - } + }; componentDidUpdate(prevProps) { - if (this.props.avatarId !== prevProps.avatarId) { + if (this.props.avatarId !== prevProps.avatarId) { // HACK - a-animation ought to restart the animation when the `to` attribute changes, but it doesn't // so we need to force it here. - const currRot = this.animation.parentNode.getAttribute('rotation'); - this.animation.setAttribute('from', `${currRot.x} ${currRot.y} ${currRot.z}`); + const currRot = this.animation.parentNode.getAttribute("rotation"); + this.animation.setAttribute("from", `${currRot.x} ${currRot.y} ${currRot.z}`); this.animation.stop(); this.animation.handleMixinUpdate(); this.animation.start(); } } - render () { + render() { const avatarAssets = this.props.avatars.map(avatar => ( - <a-progressive-asset - id={avatar.id} - key={avatar.id} - response-type="arraybuffer" - high-src={`${avatar.models.high}`} - low-src={`${avatar.models.low}`} - ></a-progressive-asset> + <a-asset-item id={avatar.id} key={avatar.id} response-type="arraybuffer" src={`${avatar.model}`} /> )); const avatarEntities = this.props.avatars.map((avatar, i) => ( <a-entity key={avatar.id} position="0 0 0" rotation={`0 ${360 * -i / this.props.avatars.length} 0`}> - <a-gltf-entity position="0 0 5" rotation="0 0 0" src={'#' + avatar.id} inflate="true"> + <a-entity position="0 0 5" rotation="0 0 0" gltf-model-plus={`src: #${avatar.id}`} inflate="true"> <template data-selector=".RootScene"> - <a-entity animation-mixer></a-entity> + <a-entity animation-mixer /> </template> <a-animation attribute="rotation" dur="2000" to={`0 ${this.getAvatarIndex() === i ? 360 : 0} 0`} - repeat="indefinite"> - </a-animation> - </a-gltf-entity> + repeat="indefinite" + /> + </a-entity> </a-entity> )); return ( <div className="avatar-selector"> - <span className="avatar-selector__loading"> - <FormattedMessage id="profile.avatar-selector.loading"/> - </span> - <a-scene vr-mode-ui="enabled: false" ref={sce => this.scene = sce}> - <a-assets> - {avatarAssets} - <a-asset-item - id="meeting-space1-mesh" - response-type="arraybuffer" - src={meetingSpace} - ></a-asset-item> - </a-assets> + <span className="avatar-selector__loading"> + <FormattedMessage id="profile.avatar-selector.loading" /> + </span> + <a-scene vr-mode-ui="enabled: false" ref={sce => (this.scene = sce)}> + <a-assets> + {avatarAssets} + <a-asset-item id="meeting-space1-mesh" response-type="arraybuffer" src={meetingSpace} /> + </a-assets> - <a-entity> - <a-animation - ref={anm => this.animation = anm} - attribute="rotation" - dur="1000" - easing="ease-out" - to={`0 ${360 * this.getAvatarIndex() / this.props.avatars.length + 180} 0`}> - </a-animation> - {avatarEntities} - </a-entity> + <a-entity> + <a-animation + ref={anm => (this.animation = anm)} + attribute="rotation" + dur="1000" + easing="ease-out" + to={`0 ${360 * this.getAvatarIndex() / this.props.avatars.length + 180} 0`} + /> + {avatarEntities} + </a-entity> - <a-entity position="0 1.5 -5.6" rotation="-10 180 0" camera></a-entity> + <a-entity position="0 1.5 -5.6" rotation="-10 180 0" camera /> - <a-entity - hide-when-quality="low" - light="type: directional; color: #F9FFCE; intensity: 0.6" - position="0 5 -15" - ></a-entity> - <a-entity - hide-when-quality="low" - light="type: ambient; color: #FFF" - ></a-entity> - <a-gltf-entity - id="meeting-space" - src="#meeting-space1-mesh" - position="0 0 0" - ></a-gltf-entity> - </a-scene> - <button className="avatar-selector__previous-button" onClick={this.emitChangeToPrevious}> - <FontAwesomeIcon icon={faAngleLeft} /> - </button> - <button className="avatar-selector__next-button" onClick={this.emitChangeToNext}> - <FontAwesomeIcon icon={faAngleRight} /> - </button> + <a-entity + hide-when-quality="low" + light="type: directional; color: #F9FFCE; intensity: 0.6" + position="0 5 -15" + /> + <a-entity hide-when-quality="low" light="type: ambient; color: #FFF" /> + <a-entity id="meeting-space" gltf-model-plus="src: #meeting-space1-mesh" position="0 0 0" /> + </a-scene> + <button className="avatar-selector__previous-button" onClick={this.emitChangeToPrevious}> + <FontAwesomeIcon icon={faAngleLeft} /> + </button> + <button className="avatar-selector__next-button" onClick={this.emitChangeToNext}> + <FontAwesomeIcon icon={faAngleRight} /> + </button> </div> ); } diff --git a/src/react-components/entry-buttons.js b/src/react-components/entry-buttons.js index e567c6e1111f362ab4175087794af1f38a6a1c24..8ed8b171d6e7ec08d82d59af2c5314d9353a7910 100644 --- a/src/react-components/entry-buttons.js +++ b/src/react-components/entry-buttons.js @@ -1,28 +1,28 @@ -import React, { Component } from "react"; +import React from "react"; import { FormattedMessage } from "react-intl"; import PropTypes from "prop-types"; -import MobileDetect from 'mobile-detect'; +import MobileDetect from "mobile-detect"; -import MobileScreenEntryImg from '../assets/images/mobile_screen_entry.svg'; -import DesktopScreenEntryImg from '../assets/images/desktop_screen_entry.svg'; -import GenericVREntryImg from '../assets/images/generic_vr_entry.svg'; -import GearVREntryImg from '../assets/images/gearvr_entry.svg'; -import DaydreamEntyImg from '../assets/images/daydream_entry.svg'; +import MobileScreenEntryImg from "../assets/images/mobile_screen_entry.svg"; +import DesktopScreenEntryImg from "../assets/images/desktop_screen_entry.svg"; +import GenericVREntryImg from "../assets/images/generic_vr_entry.svg"; +import GearVREntryImg from "../assets/images/gearvr_entry.svg"; +import DaydreamEntyImg from "../assets/images/daydream_entry.svg"; const mobiledetect = new MobileDetect(navigator.userAgent); -const EntryButton = (props) => ( - <div className="entry-button" onClick={ props.onClick }> - <img src={props.iconSrc} className="entry-button__icon"/> +const EntryButton = props => ( + <div className="entry-button" onClick={props.onClick}> + <img src={props.iconSrc} className="entry-button__icon" /> <div className="entry-button__label"> <div className="entry-button__label__contents"> <span> - <FormattedMessage id={ props.prefixMessageId }/> + <FormattedMessage id={props.prefixMessageId} /> </span> <span className="entry-button--bolded"> - <FormattedMessage id={ props.mediumMessageId }/> + <FormattedMessage id={props.mediumMessageId} /> </span> - { props.subtitle && (<div className="entry-button__subtitle">{props.subtitle}</div>) } + {props.subtitle && <div className="entry-button__subtitle">{props.subtitle}</div>} </div> </div> </div> @@ -34,20 +34,20 @@ EntryButton.propTypes = { prefixMessageId: PropTypes.string, mediumMessageId: PropTypes.string, subtitle: PropTypes.string -} +}; -export const TwoDEntryButton = (props) => { +export const TwoDEntryButton = props => { const entryButtonProps = { ...props, iconSrc: mobiledetect.mobile() ? MobileScreenEntryImg : DesktopScreenEntryImg, prefixMessageId: "entry.screen-prefix", - mediumMessageId: mobiledetect.mobile() ? "entry.mobile-screen" : "entry.desktop-screen" + mediumMessageId: mobiledetect.mobile() ? "entry.mobile-screen" : "entry.desktop-screen" }; - return (<EntryButton {...entryButtonProps}/>); -} + return <EntryButton {...entryButtonProps} />; +}; -export const GenericEntryButton = (props) => { +export const GenericEntryButton = props => { const entryButtonProps = { ...props, iconSrc: GenericVREntryImg, @@ -55,10 +55,10 @@ export const GenericEntryButton = (props) => { mediumMessageId: "entry.generic-medium" }; - return (<EntryButton {...entryButtonProps}/>); + return <EntryButton {...entryButtonProps} />; }; -export const GearVREntryButton = (props) => { +export const GearVREntryButton = props => { const entryButtonProps = { ...props, iconSrc: GearVREntryImg, @@ -66,10 +66,10 @@ export const GearVREntryButton = (props) => { mediumMessageId: "entry.gearvr-medium" }; - return (<EntryButton {...entryButtonProps}/>); + return <EntryButton {...entryButtonProps} />; }; -export const DaydreamEntryButton = (props) => { +export const DaydreamEntryButton = props => { const entryButtonProps = { ...props, iconSrc: DaydreamEntyImg, @@ -77,6 +77,5 @@ export const DaydreamEntryButton = (props) => { mediumMessageId: "entry.daydream-medium" }; - return (<EntryButton {...entryButtonProps}/>); + return <EntryButton {...entryButtonProps} />; }; - diff --git a/src/react-components/home-root.js b/src/react-components/home-root.js new file mode 100644 index 0000000000000000000000000000000000000000..984a3d44c2fbb29973c3f1ed9dee4395d015bfa2 --- /dev/null +++ b/src/react-components/home-root.js @@ -0,0 +1,136 @@ +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { IntlProvider, FormattedMessage, addLocaleData } from "react-intl"; +import en from "react-intl/locale-data/en"; +import homeVideo from "../assets/video/home.webm"; + +import HubCreatePanel from "./hub-create-panel.js"; + +const navigatorLang = (navigator.languages && navigator.languages[0]) || navigator.language || navigator.userLanguage; + +const lang = navigatorLang.toLowerCase().split(/[_-]+/)[0]; + +import localeData from "../assets/translations.data.json"; +addLocaleData([...en]); + +const messages = localeData[lang] || localeData.en; + +const ENVIRONMENT_URLS = [ + `${document.location.protocol}//${document.location.host}/assets/environments/cliff_meeting_space/bundle.json` +]; + +class HomeRoot extends Component { + static propTypes = { + intl: PropTypes.object + }; + + state = { + environments: [] + }; + + componentDidMount() { + this.loadEnvironments(); + document.querySelector("#background-video").playbackRate = 0.5; + } + + loadEnvironments = () => { + const environments = []; + + const environmentLoads = ENVIRONMENT_URLS.map(src => + (async () => { + const res = await fetch(src); + const data = await res.json(); + data.bundle_url = src; + environments.push(data); + })() + ); + + Promise.all(environmentLoads).then(() => this.setState({ environments })); + }; + + render() { + return ( + <IntlProvider locale={lang} messages={messages}> + <div className="home"> + <div className="main-content"> + <div className="header-content"> + <div className="header-content__title"> + <img className="header-content__title__name" src="../assets/images/logo.svg" /> + <div className="header-content__title__preview">preview</div> + </div> + <div className="header-content__experiment"> + <div className="header-content__experiment__container"> + <img src="../assets/images/webvr_cube.svg" className="header-content__experiment__icon" /> + <div className="header-content__experiment__info"> + <div className="header-content__experiment__info__header"> + <span> + <FormattedMessage id="home.webvr_disclaimer_pre" /> + </span> + <span style={{ fontWeight: "bold" }}>WebVR</span> + <span> + <FormattedMessage id="home.webvr_disclaimer_post" /> + </span> + <span> + <a rel="noopener noreferrer" target="_blank" href="https://blog.mozvr.com"> + <FormattedMessage id="home.webvr_disclaimer_mr_team" /> + </a> + </span> + </div> + + <div> + <a + className="header-content__experiment__info__link" + rel="noopener noreferrer" + target="_blank" + href="https://github.com/mozilla/mr-social-client" + > + <FormattedMessage id="home.view_source" /> + </a> + </div> + </div> + </div> + </div> + </div> + <div className="hero-content"> + <div className="hero-content__container"> + <div className="hero-content__container__title"> + <FormattedMessage id="home.hero_title" /> + </div> + <div className="hero-content__container__subtitle"> + <FormattedMessage id="home.hero_subtitle" /> + </div> + </div> + <div className="hero-content__create"> + {this.state.environments.length > 0 && <HubCreatePanel environments={this.state.environments} />} + </div> + </div> + <div className="footer-content"> + <div className="footer-content__links"> + <div className="footer-content__links__top"> + <a className="footer-content__links__link" rel="noopener noreferrer" target="_blank" href="#"> + <FormattedMessage id="home.join_on_slack" /> + </a> + <a className="footer-content__links__link" rel="noopener noreferrer" target="_blank" href="#"> + <FormattedMessage id="home.get_updates" /> + </a> + <a className="footer-content__links__link" rel="noopener noreferrer" target="_blank" href="#"> + <FormattedMessage id="home.report_issue" /> + </a> + </div> + <div className="footer-content__links__bottom"> + <FormattedMessage id="home.made_with_love" /> + <span style={{ fontWeight: "bold", color: "white" }}>moz://a</span> + </div> + </div> + </div> + </div> + <video playsInline autoPlay muted loop className="background-video" id="background-video"> + <source src={homeVideo} type="video/webm" /> + </video> + </div> + </IntlProvider> + ); + } +} + +export default HomeRoot; diff --git a/src/react-components/hub-create-panel.js b/src/react-components/hub-create-panel.js new file mode 100644 index 0000000000000000000000000000000000000000..f117ead03f85bc0116619e14c2c3f3d7505b8ba5 --- /dev/null +++ b/src/react-components/hub-create-panel.js @@ -0,0 +1,189 @@ +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { injectIntl, FormattedMessage } from "react-intl"; +import { generateHubName } from "../utils/name-generation"; +import classNames from "classnames"; +import faAngleLeft from "@fortawesome/fontawesome-free-solid/faAngleLeft"; +import faAngleRight from "@fortawesome/fontawesome-free-solid/faAngleRight"; +import FontAwesomeIcon from "@fortawesome/react-fontawesome"; + +const HUB_NAME_PATTERN = "^[A-Za-z0-9-'\":!@#$%^&*(),.?~ ]{4,64}$"; + +class HubCreatePanel extends Component { + static propTypes = { + intl: PropTypes.object, + environments: PropTypes.array + }; + + constructor(props) { + super(props); + + this.state = { + name: generateHubName(), + environmentIndex: Math.floor(Math.random() * props.environments.length), + + // HACK: expand on small screens by default to ensure scene selection possible. + // Eventually this could/should be done via media queries. + expanded: window.innerWidth < 420 + }; + } + + createHub = async e => { + e.preventDefault(); + const environment = this.props.environments[this.state.environmentIndex]; + + const payload = { + hub: { name: this.state.name, default_environment_gltf_bundle_url: environment.bundle_url } + }; + + const res = await fetch("/api/v1/hubs", { + body: JSON.stringify(payload), + headers: { "content-type": "application/json" }, + method: "POST" + }); + + const hub = await res.json(); + document.location = hub.url; + }; + + isHubNameValid = () => { + const hubAlphaPattern = "[A-Za-z0-9]{4}"; + return new RegExp(HUB_NAME_PATTERN).test(this.state.name) && new RegExp(hubAlphaPattern).test(this.state.name); + }; + + setToEnvironmentOffset = offset => { + const numEnvs = this.props.environments.length; + + this.setState(state => ({ + environmentIndex: ((state.environmentIndex + offset) % this.props.environments.length + numEnvs) % numEnvs + })); + }; + + setToNextEnvironment = e => { + e.preventDefault(); + this.setToEnvironmentOffset(1); + }; + + setToPreviousEnvironment = e => { + e.preventDefault(); + this.setToEnvironmentOffset(-1); + }; + + shuffle = () => { + this.setState({ + name: generateHubName(), + environmentIndex: Math.floor(Math.random() * this.props.environments.length) + }); + }; + + render() { + const { formatMessage } = this.props.intl; + + if (this.props.environments.length == 0) { + return <div />; + } + + const environment = this.props.environments[this.state.environmentIndex]; + const environmentImageSrcSet = environment.meta.images.find(i => i.type === "preview-thumbnail").srcset; + + const environmentTitle = environment.meta.title; + const environmentAuthor = (environment.meta.authors || [])[0]; + + const formNameClassNames = classNames("create-panel__form__name", { + "create-panel__form__name--expanded": this.state.expanded + }); + + return ( + <form onSubmit={this.createHub}> + <div className="create-panel"> + {!this.state.expanded && ( + <div className="create-panel__header"> + <FormattedMessage id="home.create_header" /> + </div> + )} + <div className="create-panel__form"> + <div + className="create-panel__form__left-container" + onClick={e => { + e.preventDefault(); + + if (this.state.expanded) { + this.shuffle(); + } else { + this.setState({ expanded: true }); + } + }} + > + <button className="create-panel__form__rotate-button"> + {this.state.expanded ? ( + <img src="../assets/images/dice_icon.svg" /> + ) : ( + <img src="../assets/images/expand_dots_icon.svg" /> + )} + </button> + </div> + <div className="create-panel__form__right-container" onClick={this.createHub}> + <button className="create-panel__form__submit-button"> + {this.isHubNameValid() ? ( + <img src="../assets/images/hub_create_button_enabled.svg" /> + ) : ( + <img src="../assets/images/hub_create_button_disabled.svg" /> + )} + </button> + </div> + {this.state.expanded && ( + <div className="create-panel__form__environment"> + <div className="create-panel__form__environment__picker"> + <img className="create-panel__form__environment__picker__image" srcSet={environmentImageSrcSet} /> + <div className="create-panel__form__environment__picker__labels"> + <div className="create-panel__form__environment__picker__labels__header"> + <span className="create-panel__form__environment__picker__labels__header__title"> + {environmentTitle} + </span> + {environmentAuthor && + environmentAuthor.name && ( + <span className="create-panel__form__environment__picker__labels__header__author"> + <FormattedMessage id="home.environment_author_by" /> + <span>{environmentAuthor.name}</span> + </span> + )} + </div> + <div className="create-panel__form__environment__picker__labels__footer"> + <FormattedMessage id="home.environment_picker_footer" /> + </div> + </div> + <div className="create-panel__form__environment__picker__controls"> + <button + className="create-panel__form__environment__picker__controls__prev" + onClick={this.setToPreviousEnvironment} + > + <FontAwesomeIcon icon={faAngleLeft} /> + </button> + + <button + className="create-panel__form__environment__picker__controls__next" + onClick={this.setToNextEnvironment} + > + <FontAwesomeIcon icon={faAngleRight} /> + </button> + </div> + </div> + </div> + )} + <input + className={formNameClassNames} + value={this.state.name} + onChange={e => this.setState({ name: e.target.value })} + onFocus={e => e.target.select()} + required + pattern={HUB_NAME_PATTERN} + title={formatMessage({ id: "home.create_name.validation_warning" })} + /> + </div> + </div> + </form> + ); + } +} + +export default injectIntl(HubCreatePanel); diff --git a/src/react-components/profile-entry-panel.js b/src/react-components/profile-entry-panel.js index 91044c27cab1955c7e35698322c618b907a3f95d..523016e25bdf1e0f127bf5a889165b1ede806322 100644 --- a/src/react-components/profile-entry-panel.js +++ b/src/react-components/profile-entry-panel.js @@ -1,21 +1,22 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { injectIntl, FormattedMessage } from 'react-intl'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { injectIntl, FormattedMessage } from "react-intl"; import { SCHEMA } from "../storage/store"; class ProfileEntryPanel extends Component { static propTypes = { store: PropTypes.object, messages: PropTypes.object, - finished: PropTypes.func - } + finished: PropTypes.func, + htmlPrefix: PropTypes.string, + intl: PropTypes.object + }; constructor(props) { super(props); - window.store = this.props.store; this.state = { display_name: this.props.store.state.profile.display_name, - avatar_id: this.props.store.state.profile.avatar_id, + avatar_id: this.props.store.state.profile.avatar_id }; this.props.store.addEventListener("statechanged", this.storeUpdated); } @@ -23,67 +24,72 @@ class ProfileEntryPanel extends Component { storeUpdated = () => { const { avatar_id, display_name } = this.props.store.state.profile; this.setState({ avatar_id, display_name }); - } + }; - saveStateAndFinish = (e) => { + saveStateAndFinish = e => { e.preventDefault(); - this.props.store.update({profile: { - display_name: this.state.display_name, - avatar_id: this.state.avatar_id - }}); + this.props.store.update({ + profile: { + display_name: this.state.display_name, + avatar_id: this.state.avatar_id + } + }); this.props.finished(); - } + }; - stopPropagation = (e) => { + stopPropagation = e => { e.stopPropagation(); - } + }; - setAvatarStateFromIframeMessage = (e) => { - if (e.source !== this.avatarSelector.contentWindow) { return; } - this.setState({avatar_id: e.data.avatarId}); - } + setAvatarStateFromIframeMessage = e => { + if (e.source !== this.avatarSelector.contentWindow) { + return; + } + this.setState({ avatar_id: e.data.avatarId }); + }; componentDidMount() { // stop propagation so that avatar doesn't move when wasd'ing during text input. - this.nameInput.addEventListener('keydown', this.stopPropagation); - this.nameInput.addEventListener('keypress', this.stopPropagation); - this.nameInput.addEventListener('keyup', this.stopPropagation); - window.addEventListener('message', this.setAvatarStateFromIframeMessage); + this.nameInput.addEventListener("keydown", this.stopPropagation); + this.nameInput.addEventListener("keypress", this.stopPropagation); + this.nameInput.addEventListener("keyup", this.stopPropagation); + window.addEventListener("message", this.setAvatarStateFromIframeMessage); } componentWillUnmount() { - this.props.store.removeEventListener('statechanged', this.storeUpdated); - this.nameInput.removeEventListener('keydown', this.stopPropagation); - this.nameInput.removeEventListener('keypress', this.stopPropagation); - this.nameInput.removeEventListener('keyup', this.stopPropagation); - window.removeEventListener('message', this.setAvatarStateFromIframeMessage); + this.props.store.removeEventListener("statechanged", this.storeUpdated); + this.nameInput.removeEventListener("keydown", this.stopPropagation); + this.nameInput.removeEventListener("keypress", this.stopPropagation); + this.nameInput.removeEventListener("keyup", this.stopPropagation); + window.removeEventListener("message", this.setAvatarStateFromIframeMessage); } - render () { + render() { const { formatMessage } = this.props.intl; return ( <div className="profile-entry"> <form onSubmit={this.saveStateAndFinish}> - <div className="profile-entry__box profile-entry__box--darkened"> - <div className="profile-entry__subtitle"> - <FormattedMessage id="profile.header"/> + <div className="profile-entry__box profile-entry__box--darkened"> + <div className="profile-entry__subtitle"> + <FormattedMessage id="profile.header" /> + </div> + <input + className="profile-entry__form-field-text" + value={this.state.display_name} + onChange={e => this.setState({ display_name: e.target.value })} + required + pattern={SCHEMA.definitions.profile.properties.display_name.pattern} + title={formatMessage({ id: "profile.display_name.validation_warning" })} + ref={inp => (this.nameInput = inp)} + /> + <iframe + className="profile-entry__avatar-selector" + src={`/${this.props.htmlPrefix}avatar-selector.html#avatar_id=${this.state.avatar_id}`} + ref={ifr => (this.avatarSelector = ifr)} + /> + <input className="profile-entry__form-submit" type="submit" value={formatMessage({ id: "profile.save" })} /> </div> - <input - className="profile-entry__form-field-text" - value={this.state.display_name} onChange={(e) => this.setState({display_name: e.target.value})} - required pattern={SCHEMA.definitions.profile.properties.display_name.pattern} - title={formatMessage({ id: "profile.display_name.validation_warning" })} - ref={inp => this.nameInput = inp}/> - <iframe - className="profile-entry__avatar-selector" - src={ - /* HACK: Have to account for the smoke test server like this. Feels wrong though. */ - `${/smoke/i.test(location.hostname) ? 'smoke-' : ''}avatar-selector.html#avatar_id=${this.state.avatar_id}` - } - ref={ifr => this.avatarSelector = ifr}></iframe> - <input className="profile-entry__form-submit" type="submit" value={formatMessage({ id: "profile.save" }) }/> - </div> </form> </div> ); diff --git a/src/react-components/profile-info-header.js b/src/react-components/profile-info-header.js index 6b9e82bb124e931dbf59b0867eaa3e36aae431f2..43ec49291007089d54e1d1a52c7a586e766e5fb4 100644 --- a/src/react-components/profile-info-header.js +++ b/src/react-components/profile-info-header.js @@ -1,4 +1,4 @@ -import React, { Component } from "react"; +import React from "react"; import PropTypes from "prop-types"; export const ProfileInfoHeader = props => ( diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index 26ffdfbf3d55227218ea4c36f3752fbe888461df..946784e9e1f6e18aa6a412a86d350f39a1772a24 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -3,7 +3,6 @@ import PropTypes from "prop-types"; import classNames from "classnames"; import { VR_DEVICE_AVAILABILITY } from "../utils/vr-caps-detect"; import queryString from "query-string"; -import { SCHEMA } from "../storage/store"; import MobileDetect from "mobile-detect"; import { IntlProvider, FormattedMessage, addLocaleData } from "react-intl"; import en from "react-intl/locale-data/en"; @@ -34,13 +33,6 @@ const ENTRY_STEPS = { finished: "finished" }; -const HMD_MIC_REGEXES = [/\Wvive\W/i, /\Wrift\W/i]; - -async function grantedMicLabels() { - const mediaDevices = await navigator.mediaDevices.enumerateDevices(); - return mediaDevices.filter(d => d.label && d.kind === "audioinput").map(d => d.label); -} - // This is a list of regexes that match the microphone labels of HMDs. // // If entering VR mode, and if any of these regexes match an audio device, @@ -49,19 +41,26 @@ async function grantedMicLabels() { // // Note that this doesn't have to be exhaustive: if no devices match any regex // then we rely upon the user to select the proper mic. -const VR_DEVICE_MIC_LABEL_REGEXES = []; +const HMD_MIC_REGEXES = [/\Wvive\W/i, /\Wrift\W/i]; + +async function grantedMicLabels() { + const mediaDevices = await navigator.mediaDevices.enumerateDevices(); + return mediaDevices.filter(d => d.label && d.kind === "audioinput").map(d => d.label); +} const AUTO_EXIT_TIMER_SECONDS = 10; class UIRoot extends Component { static propTypes = { enterScene: PropTypes.func, + exitScene: PropTypes.func, concurrentLoadDetector: PropTypes.object, disableAutoExitOnConcurrentLoad: PropTypes.bool, forcedVREntryType: PropTypes.string, enableScreenSharing: PropTypes.bool, store: PropTypes.object, - scene: PropTypes.object + scene: PropTypes.object, + htmlPrefix: PropTypes.object }; state = { @@ -88,10 +87,12 @@ class UIRoot extends Component { autoExitTimerInterval: null, secondsRemainingBeforeAutoExit: Infinity, - sceneLoaded: false, + initialEnvironmentLoaded: false, exited: false, - showProfileEntry: false + showProfileEntry: false, + + janusRoomId: null }; componentDidMount() { @@ -118,6 +119,7 @@ class UIRoot extends Component { muted: this.props.scene.is("muted") }); }; + toggleMute = () => { this.props.scene.emit("action_mute"); }; @@ -129,6 +131,10 @@ class UIRoot extends Component { this.enterDaydream(); } else if (this.props.forcedVREntryType === "gearvr") { this.enterGearVR(); + } else if (this.props.forcedVREntryType === "vr") { + this.enterVR(); + } else if (this.props.forcedVREntryType === "2d") { + this.enter2D(); } }; @@ -210,22 +216,19 @@ class UIRoot extends Component { hasGrantedMicPermissions = async () => { if (this.state.requestedScreen) { - // There is no way to tell if you've granted mic permissions in a previous session if we've + // There is no way to tell if you've granted mic permissions in a previous session if we've // already prompted for screen sharing permissions, so we have to assume that we've never granted permissions. - // Fortunately, if you *have* granted permissions permanently, there won't be a second browser prompt, but we + // Fortunately, if you *have* granted permissions permanently, there won't be a second browser prompt, but we // can't determine that before hand. // See https://bugzilla.mozilla.org/show_bug.cgi?id=1449783 for a potential solution in the future. return false; - } - else { + } else { // If we haven't requested the screen in this session, check if we've granted permissions in a previous session. return (await grantedMicLabels()).length > 0; } - } + }; performDirectEntryFlow = async enterInVR => { - this.startTestTone(); - this.setState({ enterInVR }); const hasGrantedMic = await this.hasGrantedMicPermissions(); @@ -234,7 +237,6 @@ class UIRoot extends Component { await this.setMediaStreamToDefault(); await this.beginAudioSetup(); } else { - this.stopTestTone(); this.setState({ entryStep: ENTRY_STEPS.mic_grant }); } }; @@ -248,35 +250,39 @@ class UIRoot extends Component { }; enterGearVR = async () => { - this.exit(); + if (this.state.availableVREntryTypes.gearvr === VR_DEVICE_AVAILABILITY.yes) { + await this.performDirectEntryFlow(true); + } else { + this.exit(); - // Launch via Oculus Browser - const qs = queryString.parse(document.location.search); - qs.vr_entry_type = "gearvr"; // Auto-choose 'gearvr' after landing in Oculus Browser + // Launch via Oculus Browser + const location = window.location; + const qs = queryString.parse(location.search); + qs.vr_entry_type = "gearvr"; // Auto-choose 'gearvr' after landing in Oculus Browser - const ovrwebUrl = `ovrweb://${document.location.protocol || "http:"}//${document.location.host}${document.location - .pathname || ""}?${queryString.stringify(qs)}#{document.location.hash || ""}`; + const ovrwebUrl = + `ovrweb://${location.protocol || "http:"}//${location.host}` + + `${location.pathname || ""}?${queryString.stringify(qs)}#${location.hash || ""}`; - document.location = ovrwebUrl; + window.location = ovrwebUrl; + } }; enterDaydream = async () => { - const loc = document.location; - if (this.state.availableVREntryTypes.daydream == VR_DEVICE_AVAILABILITY.maybe) { this.exit(); // We are not in mobile chrome, so launch into chrome via an Intent URL - const qs = queryString.parse(document.location.search); + const location = window.location; + const qs = queryString.parse(location.search); qs.vr_entry_type = "daydream"; // Auto-choose 'daydream' after landing in chrome - const intentUrl = `intent://${document.location.host}${document.location.pathname || ""}?${queryString.stringify( - qs - )}#Intent;scheme=${(document.location.protocol || "http:").replace( - ":", - "" - )};action=android.intent.action.VIEW;package=com.android.chrome;end;`; - document.location = intentUrl; + const intentUrl = + `intent://${location.host}${location.pathname || ""}?` + + `${queryString.stringify(qs)}#Intent;scheme=${(location.protocol || "http:").replace(":", "")};` + + `action=android.intent.action.VIEW;package=com.android.chrome;end;`; + + window.location = intentUrl; } else { await this.performDirectEntryFlow(true); } @@ -286,85 +292,121 @@ class UIRoot extends Component { const constraints = { audio: { deviceId: { exact: [ev.target.value] } } }; await this.fetchAudioTrack(constraints); await this.setupNewMediaStream(); - } + }; setMediaStreamToDefault = async () => { - await this.fetchAudioTrack({ audio: true }); + let hasAudio = false; + const { lastUsedMicDeviceId } = this.props.store.state; + + // Try to fetch last used mic, if there was one. + if (lastUsedMicDeviceId) { + hasAudio = await this.fetchAudioTrack({ audio: { deviceId: { ideal: lastUsedMicDeviceId } } }); + } else { + hasAudio = await this.fetchAudioTrack({ audio: true }); + } + await this.setupNewMediaStream(); - } + + return { hasAudio }; + }; setStateAndRequestScreen = async e => { const checked = e.target.checked; await this.setState({ requestedScreen: true, shareScreen: checked }); if (checked) { - this.fetchVideoTrack({ video: { - mediaSource: "screen", - // Work around BMO 1449832 by calculating the width. This will break for multi monitors if you share anything - // other than your current monitor that has a different aspect ratio. - width: screen.width / screen.height * 720, - height: 720, - frameRate: 30 - } }); - } - else { + this.fetchVideoTrack({ + video: { + mediaSource: "screen", + // Work around BMO 1449832 by calculating the width. This will break for multi monitors if you share anything + // other than your current monitor that has a different aspect ratio. + width: screen.width / screen.height * 720, + height: 720, + frameRate: 30 + } + }); + } else { this.setState({ videoTrack: null }); } - } + }; fetchVideoTrack = async constraints => { const mediaStream = await navigator.mediaDevices.getUserMedia(constraints); this.setState({ videoTrack: mediaStream.getVideoTracks()[0] }); - } + }; fetchAudioTrack = async constraints => { if (this.state.audioTrack) { this.state.audioTrack.stop(); } - const mediaStream = await navigator.mediaDevices.getUserMedia(constraints); - this.setState({ audioTrack: mediaStream.getAudioTracks()[0] }); - } - setupNewMediaStream = async constraints => { + try { + const mediaStream = await navigator.mediaDevices.getUserMedia(constraints); + this.setState({ audioTrack: mediaStream.getAudioTracks()[0] }); + return true; + } catch (e) { + // Error fetching audio track, most likely a permission denial. + this.setState({ audioTrack: null }); + return false; + } + }; + + setupNewMediaStream = async () => { const mediaStream = new MediaStream(); - // we should definitely have an audioTrack at this point. - mediaStream.addTrack(this.state.audioTrack); + await this.fetchMicDevices(); if (this.state.videoTrack) { mediaStream.addTrack(this.state.videoTrack); } - const AudioContext = window.AudioContext || window.webkitAudioContext; - const audioContext = new AudioContext(); - const source = audioContext.createMediaStreamSource(mediaStream); - const analyzer = audioContext.createAnalyser(); - const levels = new Uint8Array(analyzer.fftSize); + // we should definitely have an audioTrack at this point unless they denied mic access + if (this.state.audioTrack) { + mediaStream.addTrack(this.state.audioTrack); + + const AudioContext = window.AudioContext || window.webkitAudioContext; + const micLevelAudioContext = new AudioContext(); + const micSource = micLevelAudioContext.createMediaStreamSource(mediaStream); + const analyzer = micLevelAudioContext.createAnalyser(); + const levels = new Uint8Array(analyzer.fftSize); + + micSource.connect(analyzer); + + const micUpdateInterval = setInterval(() => { + analyzer.getByteTimeDomainData(levels); - source.connect(analyzer); + let v = 0; - const micUpdateInterval = setInterval(() => { - analyzer.getByteTimeDomainData(levels); + for (let x = 0; x < levels.length; x++) { + v = Math.max(levels[x] - 127, v); + } - let v = 0; + const level = v / 128.0; + this.micLevelMovingAverage.push(Date.now(), level); + this.setState({ micLevel: this.micLevelMovingAverage.movingAverage() }); + }, 50); - for (let x = 0; x < levels.length; x++) { - v = Math.max(levels[x] - 127, v); + const micDeviceId = this.micDeviceIdForMicLabel(this.micLabelForMediaStream(mediaStream)); + + if (micDeviceId) { + this.props.store.update({ lastUsedMicDeviceId: micDeviceId }); } - const level = v / 128.0; - this.micLevelMovingAverage.push(Date.now(), level); - this.setState({ micLevel: this.micLevelMovingAverage.movingAverage() }); - }, 50); + this.setState({ micLevelAudioContext, micUpdateInterval }); + } - this.setState({ mediaStream, micUpdateInterval }); + this.setState({ mediaStream }); }; onMicGrantButton = async () => { if (this.state.entryStep == ENTRY_STEPS.mic_grant) { - await this.setMediaStreamToDefault(); - this.setState({ entryStep: ENTRY_STEPS.mic_granted }); + const { hasAudio } = await this.setMediaStreamToDefault(); + + if (hasAudio) { + this.setState({ entryStep: ENTRY_STEPS.mic_granted }); + } else { + await this.beginAudioSetup(); + } } else { - this.startTestTone(); await this.beginAudioSetup(); } }; @@ -374,16 +416,22 @@ class UIRoot extends Component { }; beginAudioSetup = async () => { - await this.fetchMicDevices(); + this.startTestTone(); this.setState({ entryStep: ENTRY_STEPS.audio_setup }); }; - fetchMicDevices = async () => { - const mediaDevices = await navigator.mediaDevices.enumerateDevices(); - this.setState({ - micDevices: mediaDevices. - filter(d => d.kind === "audioinput"). - map(d => ({ deviceId: d.deviceId, label: d.label })) + fetchMicDevices = () => { + return new Promise(resolve => { + navigator.mediaDevices.enumerateDevices().then(mediaDevices => { + this.setState( + { + micDevices: mediaDevices + .filter(d => d.kind === "audioinput") + .map(d => ({ deviceId: d.deviceId, label: d.label })) + }, + resolve + ); + }); }); }; @@ -399,21 +447,24 @@ class UIRoot extends Component { return !!this.state.micDevices.find(d => HMD_MIC_REGEXES.find(r => d.label.match(r))); }; + micLabelForMediaStream = mediaStream => { + return (mediaStream && mediaStream.getAudioTracks().length > 0 && mediaStream.getAudioTracks()[0].label) || ""; + }; + selectedMicLabel = () => { - return ( - (this.state.mediaStream && - this.state.mediaStream.getAudioTracks().length > 0 && - this.state.mediaStream.getAudioTracks()[0].label) || - "" - ); + return this.micLabelForMediaStream(this.state.mediaStream); + }; + + micDeviceIdForMicLabel = label => { + return this.state.micDevices.filter(d => d.label === label).map(d => d.deviceId)[0]; }; selectedMicDeviceId = () => { - return this.state.micDevices.filter(d => d.label === this.selectedMicLabel).map(d => d.deviceId)[0]; + return this.micDeviceIdForMicLabel(this.selectedMicLabel()); }; onAudioReadyButton = () => { - this.props.enterScene(this.state.mediaStream, this.state.enterInVR); + this.props.enterScene(this.state.mediaStream, this.state.enterInVR, this.state.janusRoomId); const mediaStream = this.state.mediaStream; @@ -428,11 +479,17 @@ class UIRoot extends Component { } this.stopTestTone(); + + if (this.state.micLevelAudioContext) { + this.state.micLevelAudioContext.close(); + clearInterval(this.state.micUpdateInterval); + } + this.setState({ entryStep: ENTRY_STEPS.finished }); }; render() { - if (!this.props.scene.hasLoaded || !this.state.availableVREntryTypes) { + if (!this.state.initialEnvironmentLoaded || !this.state.availableVREntryTypes || !this.state.janusRoomId) { return ( <IntlProvider locale={lang} messages={messages}> <div className="loading-panel"> @@ -468,45 +525,44 @@ class UIRoot extends Component { // Only show this in desktop firefox since other browsers/platforms will ignore the "screen" media constraint and // will attempt to share your webcam instead! - const screenSharingCheckbox = ( - this.props.enableScreenSharing && - !mobiledetect.mobile() && - /firefox/i.test(navigator.userAgent) && - ( + const screenSharingCheckbox = this.props.enableScreenSharing && + !mobiledetect.mobile() && + /firefox/i.test(navigator.userAgent) && ( <label className="entry-panel__screen-sharing"> - <input className="entry-panel__screen-sharing-checkbox" type="checkbox" + <input + className="entry-panel__screen-sharing-checkbox" + type="checkbox" value={this.state.shareScreen} onChange={this.setStateAndRequestScreen} /> <FormattedMessage id="entry.enable-screen-sharing" /> </label> - ) - ); + ); - const entryPanel = + const entryPanel = this.state.entryStep === ENTRY_STEPS.start ? ( <div className="entry-panel"> <TwoDEntryButton onClick={this.enter2D} /> - { this.state.availableVREntryTypes.generic !== VR_DEVICE_AVAILABILITY.no && ( - <GenericEntryButton onClick={this.enterVR} /> + {this.state.availableVREntryTypes.generic !== VR_DEVICE_AVAILABILITY.no && ( + <GenericEntryButton onClick={this.enterVR} /> )} - { this.state.availableVREntryTypes.gearvr !== VR_DEVICE_AVAILABILITY.no && ( - <GearVREntryButton onClick={this.enterGearVR} /> + {this.state.availableVREntryTypes.gearvr !== VR_DEVICE_AVAILABILITY.no && ( + <GearVREntryButton onClick={this.enterGearVR} /> )} - { this.state.availableVREntryTypes.daydream !== VR_DEVICE_AVAILABILITY.no && ( + {this.state.availableVREntryTypes.daydream !== VR_DEVICE_AVAILABILITY.no && ( <DaydreamEntryButton onClick={this.enterDaydream} subtitle={ - this.state.availableVREntryTypes.daydream == VR_DEVICE_AVAILABILITY.maybe ? daydreamMaybeSubtitle : "" + this.state.availableVREntryTypes.daydream == VR_DEVICE_AVAILABILITY.maybe ? daydreamMaybeSubtitle : "" } - /> + /> )} {this.state.availableVREntryTypes.cardboard !== VR_DEVICE_AVAILABILITY.no && ( <div className="entry-panel__secondary" onClick={this.enterVR}> <FormattedMessage id="entry.cardboard" /> </div> )} - { screenSharingCheckbox } + {screenSharingCheckbox} </div> ) : null; @@ -567,11 +623,19 @@ class UIRoot extends Component { </div> <div className="audio-setup-panel__levels"> <div className="audio-setup-panel__levels__mic"> - <img - src="../assets/images/mic_level.png" - srcSet="../assets/images/mic_level@2x.png 2x" - className="audio-setup-panel__levels__mic_icon" - /> + {this.state.audioTrack ? ( + <img + src="../assets/images/mic_level.png" + srcSet="../assets/images/mic_level@2x.png 2x" + className="audio-setup-panel__levels__mic_icon" + /> + ) : ( + <img + src="../assets/images/mic_denied.png" + srcSet="../assets/images/mic_denied@2x.png 2x" + className="audio-setup-panel__levels__mic_icon" + /> + )} <img src="../assets/images/level_fill.png" srcSet="../assets/images/level_fill@2x.png 2x" @@ -593,22 +657,24 @@ class UIRoot extends Component { /> </div> </div> - <div className="audio-setup-panel__device-chooser"> - <select - className="audio-setup-panel__device-chooser__dropdown" - value={this.selectedMicDeviceId()} - onChange={this.micDeviceChanged} - > - {this.state.micDevices.map(d => ( - <option key={d.deviceId} value={d.deviceId}> - {d.label} - </option> - ))} - </select> - <div className="audio-setup-panel__device-chooser__mic-icon"> - <img src="../assets/images/mic_small.png" srcSet="../assets/images/mic_small@2x.png 2x" /> + {this.state.audioTrack && ( + <div className="audio-setup-panel__device-chooser"> + <select + className="audio-setup-panel__device-chooser__dropdown" + value={this.selectedMicDeviceId()} + onChange={this.micDeviceChanged} + > + {this.state.micDevices.map(d => ( + <option key={d.deviceId} value={d.deviceId}> + {d.label} + </option> + ))} + </select> + <div className="audio-setup-panel__device-chooser__mic-icon"> + <img src="../assets/images/mic_small.png" srcSet="../assets/images/mic_small@2x.png 2x" /> + </div> </div> - </div> + )} {this.shouldShowHmdMicWarning() && ( <div className="audio-setup-panel__hmd-mic-warning"> <img @@ -661,7 +727,11 @@ class UIRoot extends Component { <div className={dialogBoxContentsClassNames}>{dialogContents}</div> {this.state.showProfileEntry && ( - <ProfileEntryPanel finished={this.onProfileFinished} store={this.props.store} /> + <ProfileEntryPanel + finished={this.onProfileFinished} + store={this.props.store} + htmlPrefix={this.props.htmlPrefix} + /> )} </div> )} diff --git a/src/storage/store.js b/src/storage/store.js index 037e27bf1d9603a210423baa5ee6543c2ee77fdb..6f480aa76db6f14904b611cecf5b0b6b84b1f41f 100644 --- a/src/storage/store.js +++ b/src/storage/store.js @@ -4,7 +4,7 @@ import { Validator } from "jsonschema"; const LOCAL_STORE_KEY = "___mozilla_duck"; const STORE_STATE_CACHE_KEY = Symbol(); const validator = new Validator(); -import { EventTarget } from "event-target-shim" +import { EventTarget } from "event-target-shim"; // Durable (via local-storage) schema-enforced state that is meant to be consumed via forward data flow. // (Think flux but with way less incidental complexity, at least for now :)) @@ -17,7 +17,7 @@ export const SCHEMA = { additionalProperties: false, properties: { display_name: { type: "string", pattern: "^[A-Za-z0-9-]{3,32}$" }, - avatar_id: { type: "string" }, + avatar_id: { type: "string" } } } }, @@ -26,11 +26,12 @@ export const SCHEMA = { properties: { id: { type: "string", pattern: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" }, - profile: { "$ref": "#/definitions/profile" }, + profile: { $ref: "#/definitions/profile" }, + lastUsedMicDeviceId: { type: "string" } }, additionalProperties: false -} +}; export default class Store extends EventTarget { constructor() { @@ -51,15 +52,14 @@ export default class Store extends EventTarget { update(newState) { if (newState.id) { - throw "Store id is immutable."; + throw new Error("Store id is immutable."); } const finalState = { ...this.state, ...newState }; const isValid = validator.validate(finalState, SCHEMA).valid; if (!isValid) { - throw `Write of ${JSON.stringify(finalState)} to store failed schema validation.`; - return; + throw new Error(`Write of ${JSON.stringify(finalState)} to store failed schema validation.`); } localStorage.setItem(LOCAL_STORE_KEY, JSON.stringify(finalState)); diff --git a/src/systems/app-mode.js b/src/systems/app-mode.js index 421bf48abbab344b877f9805803a7dfe30472336..1ef1c2cd0d7b79c75d0738717558901370320e5b 100644 --- a/src/systems/app-mode.js +++ b/src/systems/app-mode.js @@ -188,7 +188,7 @@ AFRAME.registerComponent("vr-mode-toggle-visibility", { this.el.sceneEl.removeEventListener("exit-vr", this.updateComponentState); }, - updateComponentState(i) { + updateComponentState() { const inVRMode = this.el.sceneEl.is("vr-mode"); this.el.setAttribute("visible", inVRMode !== this.data.invert); } @@ -218,7 +218,7 @@ AFRAME.registerComponent("vr-mode-toggle-playing", { this.el.sceneEl.removeEventListener("exit-vr", this.updateComponentState); }, - updateComponentState(i) { + updateComponentState() { const componentName = this.id; const inVRMode = this.el.sceneEl.is("vr-mode"); this.el.components[componentName][inVRMode !== this.data.invert ? "play" : "pause"](); diff --git a/src/systems/personal-space-bubble.js b/src/systems/personal-space-bubble.js index 563818408f3dc4ea594f5ba2bc2eb10749dca5d2..bfd648a96fb7f98129b2cd1231f3466932ab4f93 100644 --- a/src/systems/personal-space-bubble.js +++ b/src/systems/personal-space-bubble.js @@ -2,93 +2,188 @@ const invaderPos = new AFRAME.THREE.Vector3(); const bubblePos = new AFRAME.THREE.Vector3(); AFRAME.registerSystem("personal-space-bubble", { + schema: { + debug: { default: false } + }, + init() { this.invaders = []; this.bubbles = []; }, - registerBubble(el) { - this.bubbles.push(el); + registerBubble(bubble) { + this.bubbles.push(bubble); }, - unregisterBubble(el) { - const index = this.bubbles.indexOf(el); + unregisterBubble(bubble) { + const index = this.bubbles.indexOf(bubble); if (index !== -1) { this.bubbles.splice(index, 1); } }, - registerInvader(el) { - NAF.utils.getNetworkedEntity(el).then(networkedEl => { + registerInvader(invader) { + NAF.utils.getNetworkedEntity(invader.el).then(networkedEl => { const owner = NAF.utils.getNetworkOwner(networkedEl); if (owner !== NAF.clientId) { - this.invaders.push(el); + this.invaders.push(invader); } }); }, - unregisterInvader(el) { - const index = this.invaders.indexOf(el); + unregisterInvader(invader) { + const index = this.invaders.indexOf(invader); if (index !== -1) { this.invaders.splice(index, 1); } }, + update() { + for (let i = 0; i < this.bubbles.length; i++) { + this.bubbles[i].updateDebug(); + } + + for (let i = 0; i < this.invaders.length; i++) { + this.invaders[i].updateDebug(); + } + }, + tick() { // Update matrix positions once for each space bubble and space invader - for (var i = 0; i < this.bubbles.length; i++) { - this.bubbles[i].object3D.updateMatrixWorld(true); + for (let i = 0; i < this.bubbles.length; i++) { + this.bubbles[i].el.object3D.updateMatrixWorld(true); } - for (var i = 0; i < this.invaders.length; i++) { - this.invaders[i].object3D.updateMatrixWorld(true); + for (let i = 0; i < this.invaders.length; i++) { + this.invaders[i].el.object3D.updateMatrixWorld(true); + this.invaders[i].setInvading(false); } // Loop through all of the space bubbles (usually one) - for (var i = 0; i < this.bubbles.length; i++) { + for (let i = 0; i < this.bubbles.length; i++) { const bubble = this.bubbles[i]; - bubblePos.setFromMatrixPosition(bubble.object3D.matrixWorld); - - const radius = bubble.components["personal-space-bubble"].data.radius; - const radiusSquared = radius * radius; + bubblePos.setFromMatrixPosition(bubble.el.object3D.matrixWorld); // Hide the invader if inside the bubble for (let j = 0; j < this.invaders.length; j++) { const invader = this.invaders[j]; - invaderPos.setFromMatrixPosition(invader.object3D.matrixWorld); - - const distanceSquared = bubblePos.distanceTo(invaderPos); + invaderPos.setFromMatrixPosition(invader.el.object3D.matrixWorld); - invader.object3D.visible = distanceSquared > radiusSquared; + const distanceSquared = bubblePos.distanceToSquared(invaderPos); + const radiusSum = bubble.data.radius + invader.data.radius; + if (distanceSquared < radiusSum * radiusSum) { + invader.setInvading(true); + } } } } }); +function createSphereGizmo(radius) { + const geometry = new THREE.SphereBufferGeometry(radius, 10, 10); + const wireframe = new THREE.WireframeGeometry(geometry); + const line = new THREE.LineSegments(wireframe); + line.material.opacity = 0.5; + line.material.transparent = true; + return line; +} + +// TODO: we need to come up with a more generic way of doing this as this is very specific to our avatars. +AFRAME.registerComponent("space-invader-mesh", { + schema: { + meshSelector: { type: "string" } + }, + init() { + this.targetMesh = this.el.querySelector(this.data.meshSelector).object3DMap.skinnedmesh; + } +}); + +function findInvaderMesh(entity) { + while (entity && !(entity.components && entity.components["space-invader-mesh"])) { + entity = entity.parentNode; + } + return entity && entity.components["space-invader-mesh"].targetMesh; +} + +const DEBUG_OBJ = "psb-debug"; + AFRAME.registerComponent("personal-space-invader", { + schema: { + radius: { type: "number", default: 0.1 }, + useMaterial: { default: false }, + debug: { default: false }, + invadingOpacity: { default: 0.3 } + }, + init() { - this.el.sceneEl.systems["personal-space-bubble"].registerInvader(this.el); + const system = this.el.sceneEl.systems["personal-space-bubble"]; + system.registerInvader(this); + if (this.data.useMaterial) { + const mesh = findInvaderMesh(this.el); + if (mesh) { + this.targetMaterial = mesh.material; + } + } + this.invading = false; + }, + + update() { + this.radiusSquared = this.data.radius * this.data.radius; + this.updateDebug(); + }, + + updateDebug() { + const system = this.el.sceneEl.systems["personal-space-bubble"]; + if (system.data.debug || this.data.debug) { + !this.el.object3DMap[DEBUG_OBJ] && this.el.setObject3D(DEBUG_OBJ, createSphereGizmo(this.data.radius)); + } else if (this.el.object3DMap[DEBUG_OBJ]) { + this.el.removeObject3D(DEBUG_OBJ); + } }, remove() { - this.el.sceneEl.systems["personal-space-bubble"].unregisterInvader(this.el); + this.el.sceneEl.systems["personal-space-bubble"].unregisterInvader(this); + }, + + setInvading(invading) { + if (this.targetMaterial) { + this.targetMaterial.opacity = invading ? this.data.invadingOpacity : 1; + this.targetMaterial.transparent = invading; + } else { + this.el.object3D.visible = !invading; + } + this.invading = invading; } }); AFRAME.registerComponent("personal-space-bubble", { schema: { - radius: { type: "number", default: 0.8 } + radius: { type: "number", default: 0.8 }, + debug: { default: false } }, init() { - this.system.registerBubble(this.el); + this.system.registerBubble(this); + }, + + update() { + this.radiusSquared = this.data.radius * this.data.radius; + this.updateDebug(); + }, + + updateDebug() { + if (this.system.data.debug || this.data.debug) { + !this.el.object3DMap[DEBUG_OBJ] && this.el.setObject3D(DEBUG_OBJ, createSphereGizmo(this.data.radius)); + } else if (this.el.object3DMap[DEBUG_OBJ]) { + this.el.removeObject3D(DEBUG_OBJ); + } }, remove() { - this.system.unregisterBubble(this.el); + this.system.unregisterBubble(this); } }); diff --git a/src/telemetry.js b/src/telemetry.js index 9e42e76a0a082e945189b560c4dbaf03bf0d733f..6eed4d002b96c507dcdaea78b6a5ab58f9791441 100644 --- a/src/telemetry.js +++ b/src/telemetry.js @@ -1,7 +1,7 @@ import Raven from "raven-js"; export default function registerTelemetry() { - Raven.config( - "https://f571beaf5cee4e3085e0bf436f3eb158@sentry.io/256771" - ).install(); + if (process.env.NODE_ENV === "production") { + Raven.config("https://f571beaf5cee4e3085e0bf436f3eb158@sentry.io/256771").install(); + } } diff --git a/src/utils/concurrent-load-detector.js b/src/utils/concurrent-load-detector.js index f05f619a3f51fab56a33acaec2eeb3b75e48ccea..53fc2d1fa37c1f236966211ba2abaaf4631960de 100644 --- a/src/utils/concurrent-load-detector.js +++ b/src/utils/concurrent-load-detector.js @@ -3,7 +3,7 @@ // events. const LOCAL_STORE_KEY = "___concurrent_load_detector"; -import { EventTarget } from "event-target-shim" +import { EventTarget } from "event-target-shim"; export default class ConcurrentLoadDetector extends EventTarget { constructor(instanceKey) { @@ -20,17 +20,17 @@ export default class ConcurrentLoadDetector extends EventTarget { // Check for concurrent load every second this.interval = setInterval(this._step, 1000); - } + }; stop = () => { if (this.interval) { clearInterval(this.interval); } - } + }; localStorageKey = () => { return `${LOCAL_STORE_KEY}_${this.instanceKey}`; - } + }; _step = () => { const currentState = JSON.parse(localStorage.getItem(this.localStorageKey())); @@ -40,5 +40,5 @@ export default class ConcurrentLoadDetector extends EventTarget { this.dispatchEvent(new CustomEvent("concurrentload")); this.stop(); } - } + }; } diff --git a/src/utils/dpad.js b/src/utils/dpad.js index 6f135122e6408bfdbc0a5a87d373104cbdc2e9bd..c603e75ad35438deb4311be5bdc68b64022da776 100644 --- a/src/utils/dpad.js +++ b/src/utils/dpad.js @@ -13,7 +13,7 @@ export function angleTo4Direction(angle) { export function angleTo8Direction(angle) { angle = (angle * THREE.Math.RAD2DEG + 180 + 45) % 360; - var direction = ""; + let direction = ""; if ((angle >= 0 && angle < 120) || angle >= 330) { direction += "north"; } diff --git a/src/utils/identity.js b/src/utils/identity.js index 118fe30812c1013cb64c47a335c24465a12f6636..def830cbc4df4d9d1a71b0c10d7b54c5a5610b4a 100644 --- a/src/utils/identity.js +++ b/src/utils/identity.js @@ -164,7 +164,7 @@ const names = [ ]; function selectRandom(arr) { - return arr[Math.floor(Math.random() * arr.length)] + return arr[Math.floor(Math.random() * arr.length)]; } export const avatarIds = avatars.map(av => av.id); @@ -172,7 +172,7 @@ export const avatarIds = avatars.map(av => av.id); export function generateDefaultProfile() { const name = selectRandom(names); return { - display_name: name.replace(/^./, name[0].toUpperCase()) , + display_name: name.replace(/^./, name[0].toUpperCase()), avatar_id: selectRandom(avatarIds) }; } diff --git a/src/utils/name-generation.js b/src/utils/name-generation.js new file mode 100644 index 0000000000000000000000000000000000000000..385e62bd01890e983fd3a8fc9e38ecb7fd570130 --- /dev/null +++ b/src/utils/name-generation.js @@ -0,0 +1,673 @@ +const adjectives = [ + "able", + "absolute", + "acceptable", + "acclaimed", + "accomplished", + "accurate", + "aching", + "acrobatic", + "adorable", + "adventurous", + "basic", + "belated", + "beloved", + "calm", + "candid", + "capital", + "carefree", + "caring", + "cautious", + "celebrated", + "charming", + "daring", + "darling", + "dearest", + "each", + "eager", + "early", + "earnest", + "easy", + "easygoing", + "ecstatic", + "edible", + "fabulous", + "fair", + "faithful", + "familiar", + "famous", + "fancy", + "fantastic", + "far", + "generous", + "gentle", + "genuine", + "giant", + "handmade", + "handsome", + "handy", + "happy", + "icy", + "ideal", + "identical", + "keen", + "lasting", + "lavish", + "magnificent", + "majestic", + "mammoth", + "marvelous", + "natural", + "obedient", + "palatable", + "parched", + "passionate", + "pastel", + "peaceful", + "perfect", + "perfumed", + "quaint", + "qualified", + "radiant", + "rapid", + "rare", + "safe", + "sandy", + "satisfied", + "scaly", + "scarce", + "scared", + "scary", + "scented", + "scientific", + "secret", + "sentimental", + "talkative", + "tangible", + "tart", + "tasty", + "tattered", + "teeming", + "ultimate", + "uncommon", + "unconscious", + "understated", + "warm", + "active", + "adept", + "admirable", + "admired", + "adorable", + "adored", + "advanced", + "affectionate", + "beneficial", + "best", + "better", + "big", + "cheerful", + "cheery", + "chief", + "chilly", + "classic", + "clean", + "clear", + "clever", + "decent", + "decisive", + "deep", + "defiant", + "definitive", + "delectable", + "delicious", + "elaborate", + "elastic", + "elated", + "elegant", + "elementary", + "elliptical", + "fast", + "favorable", + "favorite", + "fearless", + "gifted", + "glamorous", + "gleaming", + "glittering", + "harmonious", + "imaginative", + "immense", + "jealous", + "kind", + "leafy", + "legal", + "mature", + "mean", + "meaty", + "nautical", + "neat", + "necessary", + "needy", + "oddball", + "offbeat", + "periodic", + "perky", + "personal", + "pertinent", + "petty", + "quarterly", + "ready", + "real", + "realistic", + "reasonable", + "regal", + "serene", + "shabby", + "sharp", + "shiny", + "showy", + "shy", + "silky", + "tempting", + "tense", + "terrific", + "testy", + "thankful", + "uniform", + "unique", + "vast", + "weary", + "wee", + "welcome", + "agile", + "alarmed", + "alert", + "alive", + "bleak", + "blissful", + "blushing", + "coarse", + "colorful", + "colossal", + "comfortable", + "compassionate", + "complete", + "delightful", + "dense", + "dependable", + "dependent", + "descriptive", + "detailed", + "determined", + "devoted", + "different", + "eminent", + "emotional", + "enchanted", + "enchanting", + "energetic", + "enormous", + "fine", + "finished", + "firm", + "firsthand", + "fixed", + "flashy", + "flawless", + "glorious", + "glossy", + "golden", + "good", + "gorgeous", + "graceful", + "healthy", + "heartfelt", + "hearty", + "helpful", + "impartial", + "impressive", + "jolly", + "jovial", + "lighthearted", + "likable", + "lined", + "mellow", + "melodic", + "memorable", + "mild", + "new", + "opulent", + "playful", + "pleasant", + "pleasing", + "plump", + "plush", + "polished", + "polite", + "reliable", + "relieved", + "remarkable", + "remote", + "respectful", + "responsible", + "simple", + "simplistic", + "sizzling", + "sleepy", + "slight", + "slim", + "smart", + "smooth", + "snappy", + "snoopy", + "thirsty", + "this", + "thorough", + "those", + "thoughtful", + "united", + "vibrant", + "vicious", + "wellmade", + "whimsical", + "whirlwind", + "zesty", + "amazing", + "ambitious", + "ample", + "amused", + "amusing", + "ancient", + "angelic", + "antique", + "bold", + "bossy", + "both", + "bouncy", + "bountiful", + "complex", + "conscious", + "considerate", + "constant", + "content", + "conventional", + "cooked", + "cool", + "cooperative", + "diligent", + "dimwitted", + "direct", + "discrete", + "envious", + "essential", + "ethical", + "euphoric", + "flippant", + "fluffy", + "fluid", + "flustered", + "focused", + "fond", + "gracious", + "grand", + "grandiose", + "granular", + "grateful", + "grave", + "great", + "hidden", + "high", + "hilarious", + "homely", + "incomparable", + "incredible", + "infamous", + "joyful", + "lively", + "loathsome", + "lonely", + "long", + "milky", + "mindless", + "miniature", + "minor", + "misty", + "next", + "nice", + "nifty", + "nimble", + "orderly", + "organic", + "ornate", + "popular", + "posh", + "positive", + "potable", + "powerful", + "powerless", + "precious", + "present", + "prestigious", + "quick", + "rewarding", + "rich", + "right", + "sociable", + "soft", + "solid", + "some", + "sophisticated", + "soulful", + "sparkling", + "spectacular", + "speedy", + "spicy", + "spiffy", + "spirited", + "spiteful", + "splendid", + "spotless", + "spry", + "thrifty", + "tidy", + "tight", + "timely", + "tinted", + "unruly", + "untimely", + "violet", + "wicked", + "wide", + "wild", + "willing", + "winding", + "windy", + "zigzag", + "apprehensive", + "appropriate", + "artistic", + "assured", + "astonishing", + "bright", + "brilliant", + "bronze", + "coordinated", + "courageous", + "courteous", + "crafty", + "crazy", + "creamy", + "creative", + "crisp", + "distant", + "distinct", + "downright", + "evergreen", + "everlasting", + "every", + "evil", + "excellent", + "excitable", + "exemplary", + "exhausted", + "forthright", + "fortunate", + "fragrant", + "frank", + "free", + "frequent", + "fresh", + "friendly", + "frightened", + "frigid", + "gripping", + "grounded", + "honest", + "honorable", + "honored", + "hopeful", + "hospitable", + "hot", + "huge", + "infatuated", + "infinite", + "informal", + "insistent", + "instructive", + "juicy", + "jumbo", + "knowing", + "knowledgeable", + "longterm", + "loud", + "lovable", + "loving", + "modern", + "modest", + "moist", + "monumental", + "normal", + "notable", + "outgoing", + "precious", + "pretty", + "prickly", + "primary", + "pristine", + "private", + "prize", + "productive", + "profitable", + "quiet", + "quintessential", + "roasted", + "robust", + "square", + "squiggly", + "stable", + "staid", + "starry", + "steel", + "stimulating", + "striking", + "striped", + "strong", + "studious", + "stunning", + "tough", + "trained", + "treasured", + "tremendous", + "triangular", + "tricky", + "unused", + "unusual", + "upbeat", + "virtual", + "witty", + "wonderful", + "wooden", + "worldly", + "youthful", + "attached", + "attentive", + "attractive", + "austere", + "authentic", + "automatic", + "aware", + "awesome", + "bubbly", + "bustling", + "busy", + "buttery", + "cuddly", + "cultured", + "curly", + "curvy", + "cute", + "cylindrical", + "downright", + "dramatic", + "excited", + "exciting", + "exotic", + "experienced", + "expert", + "frosty", + "fruitful", + "full", + "fumbling", + "funny", + "fussy", + "growing", + "grown", + "gummy", + "humble", + "humongous", + "hungry", + "intelligent", + "interesting", + "known", + "kooky", + "kosher", + "loyal", + "lucky", + "luminous", + "lustrous", + "luxurious", + "multicolored", + "mysterious", + "noteworthy", + "numb", + "nutritious", + "outstanding", + "overjoyed", + "proper", + "proud", + "prudent", + "punctual", + "puny", + "pure", + "puzzled", + "puzzling", + "quirky", + "stupendous", + "sturdy", + "stylish", + "subdued", + "subtle", + "sunny", + "super", + "superb", + "supportive", + "surprised", + "sweet", + "swift", + "sympathetic", + "trivial", + "trusting", + "trustworthy", + "trusty", + "truthful", + "twin", + "usable", + "used", + "useful", + "utilized", + "vital", + "vivid", + "worried", + "worthwhile", + "worthy", + "writhing", + "wry", + "yummy", + "chocolate", + "crimson", + "cyan", + "fuchsia", + "gold", + "honeydew", + "lime", + "linen", + "magenta", + "olive", + "peru", + "salmon", + "seashell", + "sienna", + "snow", + "thistle", + "tomato", + "transparent", + "turquoise", + "violet" +]; + +const nouns = [ + "space", + "land", + "world", + "universe", + "plane", + "room", + "nation", + "plaza", + "gathering", + "meetup", + "get together", + "conclave", + "party", + "powwow", + "domain", + "dominion", + "realm", + "square", + "commons", + "park", + "cosmos", + "sphere", + "terrain", + "spot", + "zone", + "area", + "tract", + "turf", + "place", + "territory", + "volume", + "camp", + "picnic", + "outing", + "vacation", + "adventure", + "exploration", + "outing", + "walkabout", + "safari", + "venture", + "roundtable", + "barbecue", + "celebration", + "festivity", + "gala", + "shindig", + "social", + "convention", + "assembly", + "congregation", + "rendezvous", + "huddle", + "meet", + "soiree" +]; + +export function generateHubName() { + const randItem = set => { + const s = set[Math.floor(Math.random() * set.length)]; + return s.charAt(0).toUpperCase() + s.slice(1); + }; + + return `${randItem(adjectives)} ${randItem(adjectives)} ${randItem(nouns)}`; +} diff --git a/src/utils/vr-caps-detect.js b/src/utils/vr-caps-detect.js index d0c96d8fbff5b277622a28cd55cb649fc4054efd..5f312c23b7a20ca33f7f2d88fc9bd192d90df609 100644 --- a/src/utils/vr-caps-detect.js +++ b/src/utils/vr-caps-detect.js @@ -41,6 +41,7 @@ const GENERIC_ENTRY_TYPE_DEVICE_BLACKLIST = [/cardboard/i]; export async function getAvailableVREntryTypes() { const isWebVRCapableBrowser = !!navigator.getVRDisplays; const isSamsungBrowser = browser.name === "chrome" && navigator.userAgent.match(/SamsungBrowser/); + const isOculusBrowser = navigator.userAgent.match(/Oculus/); const isDaydreamCapableBrowser = !!(isWebVRCapableBrowser && browser.name === "chrome" && !isSamsungBrowser); let generic = VR_DEVICE_AVAILABILITY.no; @@ -49,7 +50,11 @@ export async function getAvailableVREntryTypes() { // We only consider GearVR support as "maybe" and never "yes". The only browser // that will detect GearVR outside of VR is Samsung Internet, and we'd prefer to launch into Oculus // Browser for now since Samsung Internet requires an additional WebVR installation + flag, so return "maybe". - const gearvr = isMaybeGearVRCompatibleDevice() ? VR_DEVICE_AVAILABILITY.maybe : VR_DEVICE_AVAILABILITY.no; + // + // If we are in Oculus Browser (ie, we are literally wearing a GearVR) then return 'yes'. + const gearvr = isMaybeGearVRCompatibleDevice() + ? isOculusBrowser ? VR_DEVICE_AVAILABILITY.yes : VR_DEVICE_AVAILABILITY.maybe + : VR_DEVICE_AVAILABILITY.no; // For daydream detection, we first check if they are on an Android compatible device, and assume they // may support daydream *unless* this browser has WebVR capabilities, in which case we can do better. diff --git a/src/vendor/GLTFLoader.js b/src/vendor/GLTFLoader.js index 0992a18dba1a474bed8999035e6c5e1376b568b3..25e397ce98c7ebc2be929195c6863338154c01bc 100644 --- a/src/vendor/GLTFLoader.js +++ b/src/vendor/GLTFLoader.js @@ -1,4 +1,5 @@ // https://github.com/mrdoob/three.js/blob/1e943ba79196737bc8505522e928595687c09425/examples/js/loaders/GLTFLoader.js +// + MOZ_alt_materials draft extension /** * @author Rich Tibbett / https://github.com/richtr @@ -14,6 +15,7 @@ THREE.GLTFLoader = ( function () { this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; this.dracoLoader = null; + this.preferredTechnique = null; } @@ -125,6 +127,12 @@ THREE.GLTFLoader = ( function () { if ( json.extensionsUsed ) { + if ( json.extensionsUsed.indexOf( EXTENSIONS.MOZ_ALT_MATERIALS ) >= 0 ) { + + extensions[ EXTENSIONS.MOZ_ALT_MATERIALS ] = new MOZAltMaterialsExtension( this.preferredTechnique ); + + } + if ( json.extensionsUsed.indexOf( EXTENSIONS.KHR_LIGHTS ) >= 0 ) { extensions[ EXTENSIONS.KHR_LIGHTS ] = new GLTFLightsExtension( json ); @@ -226,7 +234,8 @@ THREE.GLTFLoader = ( function () { KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression', KHR_LIGHTS: 'KHR_lights', KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness', - KHR_MATERIALS_UNLIT: 'KHR_materials_unlit' + KHR_MATERIALS_UNLIT: 'KHR_materials_unlit', + MOZ_ALT_MATERIALS: "MOZ_alt_materials" }; /** @@ -874,6 +883,32 @@ THREE.GLTFLoader = ( function () { } + function MOZAltMaterialsExtension ( preferredTechnique ) { + + this.name = EXTENSIONS.MOZ_ALT_MATERIALS; + this.preferredTechnique = preferredTechnique; + + } + + MOZAltMaterialsExtension.prototype.getAltMaterial = function ( materialDef, materials ) { + + var mamExtension = materialDef.extensions && materialDef.extensions[ EXTENSIONS.MOZ_ALT_MATERIALS ]; + + if ( mamExtension ) { + + if ( this.preferredTechnique && mamExtension[ this.preferredTechnique ] !== undefined ) { + + var altMaterialIndex = mamExtension[ this.preferredTechnique ]; + return materials[ altMaterialIndex ]; + + } + + return materialDef; + + } + + }; + /*********************************/ /********** INTERPOLATION ********/ /*********************************/ @@ -1855,6 +1890,13 @@ THREE.GLTFLoader = ( function () { var extensions = this.extensions; var materialDef = this.json.materials[ materialIndex ]; + if ( materialDef.extensions && materialDef.extensions[ EXTENSIONS.MOZ_ALT_MATERIALS ] ) { + + var mamExtension = extensions[ EXTENSIONS.MOZ_ALT_MATERIALS ]; + materialDef = mamExtension.getAltMaterial( materialDef, this.json.materials ); + + } + var materialType; var materialParams = {}; var materialExtensions = materialDef.extensions || {}; diff --git a/webpack.config.js b/webpack.config.js index d404babbb6d5af438271621b4e3f2fbac235a266..56e643a7260f3d8214b116ad83f0aad876c3f4a1 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -3,19 +3,20 @@ require("dotenv").config(); const fs = require("fs"); const path = require("path"); +const glob = require("glob"); const selfsigned = require("selfsigned"); const webpack = require("webpack"); const HTMLWebpackPlugin = require("html-webpack-plugin"); const ExtractTextPlugin = require("extract-text-webpack-plugin"); const _ = require("lodash"); +const SMOKE_PREFIX = "smoke-"; + function createHTTPSConfig() { if (process.env.NODE_ENV === "production") { return false; } - let https; - // Generate certs for the local webpack-dev-server. if (fs.existsSync(path.join(__dirname, "certs"))) { const key = fs.readFileSync(path.join(__dirname, "certs", "key.pem")); @@ -75,14 +76,13 @@ class LodashTemplatePlugin { const config = { entry: { - lobby: path.join(__dirname, "src", "lobby.js"), - room: path.join(__dirname, "src", "room.js"), - 'avatar-selector': path.join(__dirname, "src", "avatar-selector.js"), - onboarding: path.join(__dirname, "src", "onboarding.js") + index: path.join(__dirname, "src", "index.js"), + hub: path.join(__dirname, "src", "hub.js"), + "avatar-selector": path.join(__dirname, "src", "avatar-selector.js") }, output: { path: path.join(__dirname, "public"), - filename: "[name]-[chunkhash].js", + filename: "assets/js/[name]-[chunkhash].js", publicPath: process.env.BASE_ASSETS_PATH || "" }, mode: "development", @@ -97,7 +97,7 @@ const config = { // networked-aframe makes HEAD requests to the server for time syncing. Respond with an empty body. app.head("*", function(req, res, next) { if (req.method === "HEAD") { - res.append("Date", (new Date()).toGMTString()); + res.append("Date", new Date().toGMTString()); res.send(""); } else { next(); @@ -108,7 +108,7 @@ const config = { performance: { // Ignore media and sourcemaps when warning about file size. assetFilter(assetFilename) { - return !/\.(map|png|jpg|gif|glb)$/.test(assetFilename); + return !/\.(map|png|jpg|gif|glb|webm)$/.test(assetFilename); } }, module: { @@ -118,14 +118,7 @@ const config = { loader: "html-loader", options: { // <a-asset-item>'s src property is overwritten with the correct transformed asset url. - attrs: [ - "img:src", - "a-asset-item:src", - "a-progressive-asset:src", - "a-progressive-asset:high-src", - "a-progressive-asset:low-src", - "audio:src" - ], + attrs: ["img:src", "a-asset-item:src", "audio:src"], // You can get transformed asset urls in an html template using ${require("pathToFile.ext")} interpolate: "require" } @@ -170,7 +163,7 @@ const config = { }) }, { - test: /\.(png|jpg|gif|glb|ogg|woff2|svg)$/, + test: /\.(png|jpg|gif|glb|ogg|woff2|svg|webm)$/, use: { loader: "file-loader", options: { @@ -187,29 +180,34 @@ const config = { // Each output page needs a HTMLWebpackPlugin entry new HTMLWebpackPlugin({ filename: "index.html", - template: path.join(__dirname, "src", "lobby.html"), + template: path.join(__dirname, "src", "index.html"), // Chunks correspond with the entries you wish to include in your html template - chunks: ["lobby"] + chunks: ["index"] }), new HTMLWebpackPlugin({ - filename: "room.html", - template: path.join(__dirname, "src", "room.html"), - chunks: ["room"], + filename: "hub.html", + template: path.join(__dirname, "src", "hub.html"), + chunks: ["hub"], inject: "head" }), + // Build the GLTF asset bundle json files + ...glob.sync("src/assets/**/*.tpl").map( + f => + new HTMLWebpackPlugin({ + filename: f.replace(".tpl", "").replace("src/", ""), + template: path.join(...[__dirname, ...f.split("/")]), + chunks: [] + }) + ), new HTMLWebpackPlugin({ filename: "avatar-selector.html", template: path.join(__dirname, "src", "avatar-selector.html"), chunks: ["avatar-selector"], inject: "head" }), - new HTMLWebpackPlugin({ - filename: "onboarding.html", - template: path.join(__dirname, "src", "onboarding.html"), - chunks: ["onboarding"] - }), // Extract required css and add a content hash. - new ExtractTextPlugin("[name]-[contenthash].css", { + new ExtractTextPlugin({ + filename: "assets/stylesheets/[name]-[contenthash].css", disable: process.env.NODE_ENV !== "production" }), // Transform the output of the html-loader using _.template @@ -218,6 +216,7 @@ const config = { // expose these variables to the lodash template // ex: <%= ORIGIN_TRIAL_TOKEN %> imports: { + HTML_PREFIX: process.env.GENERATE_SMOKE_TESTS ? SMOKE_PREFIX : "", NODE_ENV: process.env.NODE_ENV, ORIGIN_TRIAL_EXPIRES: process.env.ORIGIN_TRIAL_EXPIRES, ORIGIN_TRIAL_TOKEN: process.env.ORIGIN_TRIAL_TOKEN @@ -238,14 +237,14 @@ module.exports = () => { const smokeConfig = Object.assign({}, config, { // Set the public path for to point to the correct assets on the smoke-test build. output: Object.assign({}, config.output, { - publicPath: process.env.BASE_ASSETS_PATH.replace("://", "://smoke-") + publicPath: process.env.BASE_ASSETS_PATH.replace("://", `://${SMOKE_PREFIX}`) }), // For this config plugins: config.plugins.map(plugin => { if (plugin instanceof HTMLWebpackPlugin) { return new HTMLWebpackPlugin( Object.assign({}, plugin.options, { - filename: "smoke-" + plugin.options.filename + filename: SMOKE_PREFIX + plugin.options.filename }) ); } diff --git a/yarn.lock b/yarn.lock index 8b4e312fca043c8b16a6c6d1d94e6965bc50eca0..2f9fce78b8d02099cc7bce772508fbe7a1219a36 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,70 +2,77 @@ # yarn lockfile v1 -"@babel/code-frame@7.0.0-beta.40", "@babel/code-frame@^7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.40.tgz#37e2b0cf7c56026b4b21d3927cadf81adec32ac6" +"@babel/code-frame@7.0.0-beta.42", "@babel/code-frame@^7.0.0-beta.40": + version "7.0.0-beta.42" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.42.tgz#a9c83233fa7cd06b39dc77adbb908616ff4f1962" dependencies: - "@babel/highlight" "7.0.0-beta.40" + "@babel/highlight" "7.0.0-beta.42" -"@babel/generator@7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-beta.40.tgz#ab61f9556f4f71dbd1138949c795bb9a21e302ea" +"@babel/generator@7.0.0-beta.42": + version "7.0.0-beta.42" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-beta.42.tgz#777bb50f39c94a7e57f73202d833141f8159af33" dependencies: - "@babel/types" "7.0.0-beta.40" + "@babel/types" "7.0.0-beta.42" jsesc "^2.5.1" lodash "^4.2.0" source-map "^0.5.0" trim-right "^1.0.1" -"@babel/helper-function-name@7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.40.tgz#9d033341ab16517f40d43a73f2d81fc431ccd7b6" +"@babel/helper-function-name@7.0.0-beta.42": + version "7.0.0-beta.42" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.42.tgz#b38b8f4f85168d1812c543dd700b5d549b0c4658" dependencies: - "@babel/helper-get-function-arity" "7.0.0-beta.40" - "@babel/template" "7.0.0-beta.40" - "@babel/types" "7.0.0-beta.40" + "@babel/helper-get-function-arity" "7.0.0-beta.42" + "@babel/template" "7.0.0-beta.42" + "@babel/types" "7.0.0-beta.42" -"@babel/helper-get-function-arity@7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.40.tgz#ac0419cf067b0ec16453e1274f03878195791c6e" +"@babel/helper-get-function-arity@7.0.0-beta.42": + version "7.0.0-beta.42" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.42.tgz#ad072e32f912c033053fc80478169aeadc22191e" dependencies: - "@babel/types" "7.0.0-beta.40" + "@babel/types" "7.0.0-beta.42" -"@babel/highlight@7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.40.tgz#b43d67d76bf46e1d10d227f68cddcd263786b255" +"@babel/helper-split-export-declaration@7.0.0-beta.42": + version "7.0.0-beta.42" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.42.tgz#0d0d5254220a9cc4e7e226240306b939dc210ee7" + dependencies: + "@babel/types" "7.0.0-beta.42" + +"@babel/highlight@7.0.0-beta.42": + version "7.0.0-beta.42" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.42.tgz#a502a1c0d6f99b2b0e81d468a1b0c0e81e3f3623" dependencies: chalk "^2.0.0" esutils "^2.0.2" js-tokens "^3.0.0" -"@babel/template@7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.40.tgz#034988c6424eb5c3268fe6a608626de1f4410fc8" +"@babel/template@7.0.0-beta.42": + version "7.0.0-beta.42" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.42.tgz#7186d4e70d44cdec975049ba0a73bdaf5cdee052" dependencies: - "@babel/code-frame" "7.0.0-beta.40" - "@babel/types" "7.0.0-beta.40" - babylon "7.0.0-beta.40" + "@babel/code-frame" "7.0.0-beta.42" + "@babel/types" "7.0.0-beta.42" + babylon "7.0.0-beta.42" lodash "^4.2.0" "@babel/traverse@^7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.40.tgz#d140e449b2e093ef9fe1a2eecc28421ffb4e521e" - dependencies: - "@babel/code-frame" "7.0.0-beta.40" - "@babel/generator" "7.0.0-beta.40" - "@babel/helper-function-name" "7.0.0-beta.40" - "@babel/types" "7.0.0-beta.40" - babylon "7.0.0-beta.40" - debug "^3.0.1" + version "7.0.0-beta.42" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.42.tgz#f4bf4d1e33d41baf45205e2d0463591d57326285" + dependencies: + "@babel/code-frame" "7.0.0-beta.42" + "@babel/generator" "7.0.0-beta.42" + "@babel/helper-function-name" "7.0.0-beta.42" + "@babel/helper-split-export-declaration" "7.0.0-beta.42" + "@babel/types" "7.0.0-beta.42" + babylon "7.0.0-beta.42" + debug "^3.1.0" globals "^11.1.0" invariant "^2.2.0" lodash "^4.2.0" -"@babel/types@7.0.0-beta.40", "@babel/types@^7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.40.tgz#25c3d7aae14126abe05fcb098c65a66b6d6b8c14" +"@babel/types@7.0.0-beta.42", "@babel/types@^7.0.0-beta.40": + version "7.0.0-beta.42" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.42.tgz#1e2118767684880f6963801b272fd2b3348efacc" dependencies: esutils "^2.0.2" lodash "^4.2.0" @@ -528,9 +535,9 @@ babel-core@^6.26.0: slash "^1.0.0" source-map "^0.5.6" -babel-eslint@^8.2.2: +babel-eslint@8: version "8.2.2" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-8.2.2.tgz#1102273354c6f0b29b4ea28a65f97d122296b68b" + resolved "http://registry.npmjs.org/babel-eslint/-/babel-eslint-8.2.2.tgz#1102273354c6f0b29b4ea28a65f97d122296b68b" dependencies: "@babel/code-frame" "^7.0.0-beta.40" "@babel/traverse" "^7.0.0-beta.40" @@ -1202,9 +1209,9 @@ babel-types@^6.10.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26 lodash "^4.17.4" to-fast-properties "^1.0.3" -babylon@7.0.0-beta.40, babylon@^7.0.0-beta.40: - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.40.tgz#91fc8cd56d5eb98b28e6fde41045f2957779940a" +babylon@7.0.0-beta.42, babylon@^7.0.0-beta.40: + version "7.0.0-beta.42" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.42.tgz#67cfabcd4f3ec82999d29031ccdea89d0ba99657" babylon@^6.17.3, babylon@^6.18.0: version "6.18.0" @@ -1284,7 +1291,7 @@ block-stream@*: dependencies: inherits "~2.0.0" -bluebird@^3.4.7, bluebird@^3.5.1: +bluebird@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" @@ -2383,7 +2390,7 @@ debug@2.6.9, debug@^2.0.0, debug@^2.1.0, debug@^2.2.0, debug@^2.3.3, debug@^2.6. dependencies: ms "2.0.0" -debug@^3.0.1, debug@^3.1.0: +debug@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" dependencies: @@ -2800,6 +2807,16 @@ error@^7.0.2: string-template "~0.2.1" xtend "~4.0.0" +es-abstract@^1.5.1: + version "1.11.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.11.0.tgz#cce87d518f0496893b1a30cd8461835535480681" + dependencies: + es-to-primitive "^1.1.1" + function-bind "^1.1.1" + has "^1.0.1" + is-callable "^1.1.3" + is-regex "^1.0.4" + es-abstract@^1.7.0: version "1.10.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864" @@ -3855,17 +3872,17 @@ html-minifier@^3.2.3, html-minifier@^3.5.8: relateurl "0.2.x" uglify-js "3.3.x" -html-webpack-plugin@webpack-contrib/html-webpack-plugin: - version "2.30.1" - resolved "https://codeload.github.com/webpack-contrib/html-webpack-plugin/tar.gz/1dee37e2696fd3990fe9be0a2945e465c0bb9a64" +html-webpack-plugin@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-3.1.0.tgz#6e02baaedb1e906310917f03239c793a75af2885" dependencies: - bluebird "^3.4.7" html-minifier "^3.2.3" loader-utils "^0.2.16" lodash "^4.17.3" pretty-error "^2.0.2" tapable "^1.0.0" toposort "^1.0.0" + util.promisify "1.0.0" htmlescape@^1.1.0: version "1.1.1" @@ -4853,10 +4870,6 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -material-design-lite@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/material-design-lite/-/material-design-lite-1.3.0.tgz#d004ce3fee99a1eeb74a78b8a325134a5f1171d3" - math-expression-evaluator@^1.2.14: version "1.2.17" resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" @@ -5461,6 +5474,13 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" +object.getownpropertydescriptors@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.5.1" + object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" @@ -7931,6 +7951,13 @@ util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" +util.promisify@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" + dependencies: + define-properties "^1.1.2" + object.getownpropertydescriptors "^2.0.3" + util@0.10.3, util@^0.10.3, util@~0.10.1: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"