├── .env.sample ├── .gitignore ├── .gitmodules ├── .npmignore ├── LICENSE ├── README.md ├── assets ├── css │ └── app.css ├── images │ ├── 360-demo.gif │ ├── demo.gif │ └── skymap.jpg ├── js │ ├── host-console-warning.js │ └── readme-glitch-fix.js └── models │ ├── terrain.bin │ └── terrain.gltf ├── dist ├── aframe-vimeo-component.js └── aframe-vimeo-component.min.js ├── examples ├── basic.html ├── footer.html ├── header.html ├── playback.html ├── shapes.html └── webvr-stereo.html ├── package.json ├── server.js ├── src └── index.js └── webpack.config.js /.env.sample: -------------------------------------------------------------------------------- 1 | VIMEO_TOKEN=your_vimeo_token 2 | PORT=3333 3 | DOMAIN=localhost 4 | ENV = development -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | *.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # Typescript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # macOS things 63 | .DS_Store -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ext/vimeo-threejs-player"] 2 | path = ext/vimeo-threejs-player 3 | url = https://github.com/vimeo/vimeo-threejs-player.git 4 | ignore = dirty 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | examples/* 2 | src/ 3 | spec/ 4 | docs/ 5 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2017 Vimeo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Vimeo A-Frame component

2 | 3 |

An A-Frame component for streaming video from Vimeo to WebGL/VR/AR apps

4 | 5 |

6 | Build Status 7 | Code Style 8 | Glitch Examples status 9 | License 10 |

11 | 12 |

13 | Getting started 14 | — 15 | Wiki 16 | — 17 | Stay in touch 18 |

19 | 20 | ## Examples 21 | 22 | 23 | Basic 24 | 25 | 26 | Shapes 27 | 28 | 29 | 360 WebVR 30 | 31 | 32 | Playback Control 33 | 34 | 35 | 36 | ## Features 37 | 📼 **Streaming video made simple**: The component lets you stream video hosted on Vimeo directly to your A-Frame app 38 | 39 | 🏋🏿‍ **Let us do the heavy lifting**: stream multiple resolutions including adaptive video on supported platforms for best performance and video quality 40 | 41 | 📱 **Works everywhere**: works on phones, tablets, laptops, computers, VR headsets and even underwater 42 | 43 | ## Usage 44 | To start playing and streaming video now, remix the Glitch example: 45 | 46 | 47 | Glitch remix badge 48 | 49 | 50 | The first step is to generate your own Vimeo API token. [Generate the token](https://vimeo-authy.herokuapp.com/auth/vimeo/webgl), and then copy it and paste it into the *.env* in Glitch. 51 | 52 | Almost done, go to the basic example under `examples/basic.html` 53 | and change the video id in line in to your Vimeo video id. It should look like 54 | ```html 55 | 56 | ``` 57 | Try our other examples or head to our [Getting Started](https://github.com/vimeo/aframe-vimeo-component/wiki/Getting-Started-%F0%9F%9A%80) guide to learn more 58 | 59 | > Streaming Vimeo videos requires video file access via the Vimeo API. Accessing video files is limited to [Vimeo Pro and Business customers](https://vimeo.com/upgrade). Also, for security reasons, it is necessary to run the included server which communicates with the API, if you are interested in learning more checkout our [threejs-player repository](https://github.com/vimeo/vimeo-threejs-player) which this component is based on. 60 | 61 | ## Questions 62 | For questions and support, [open a Github issue](https://github.com/vimeo/aframe-vimeo-component/issues/new). 63 | 64 | ## Contributing 65 | Get involved! Check out the [Setting up the development environment guide](https://github.com/vimeo/vimeo-threejs-player/wiki/Setting-up-the-development-environment-%F0%9F%91%B7%F0%9F%8F%BD%E2%80%8D) for how to get started. 66 | 67 | ## License 68 | This software is free software and is distributed under an [MIT License](LICENSE). 69 | -------------------------------------------------------------------------------- /assets/css/app.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #fff; 3 | color: #000; 4 | padding: 0; 5 | margin: 0; 6 | overflow: hidden; 7 | font-family: Arial, Helvetica, sans-serif; 8 | } 9 | 10 | #nav { 11 | position: fixed; 12 | left: 0; 13 | width: 250px; 14 | height: 100%; 15 | overflow: hidden; 16 | padding: 20px; 17 | z-index: 1000; 18 | background-color: white; 19 | } 20 | 21 | #nav ul { 22 | list-style-type: none; 23 | margin: 0; 24 | padding: 0; 25 | padding-left: 20px; 26 | } 27 | 28 | #nav a { 29 | color: #08C; 30 | font-size: 12px; 31 | } 32 | 33 | #examples { 34 | padding-left: 5px; 35 | } 36 | 37 | #logo { 38 | height: 30px; 39 | width: 100px; 40 | } 41 | 42 | #dominent-color { 43 | z-index: 100; 44 | width: 100px; 45 | height: 100px; 46 | position: absolute; 47 | right: 15px; 48 | top: 15px; 49 | } 50 | 51 | /* Vimeo branding */ 52 | 53 | #logo { 54 | height: 30px; 55 | } -------------------------------------------------------------------------------- /assets/images/360-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vimeo/aframe-vimeo-component/e63fc4de72316057215b966b795eb25aed04eb28/assets/images/360-demo.gif -------------------------------------------------------------------------------- /assets/images/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vimeo/aframe-vimeo-component/e63fc4de72316057215b966b795eb25aed04eb28/assets/images/demo.gif -------------------------------------------------------------------------------- /assets/images/skymap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vimeo/aframe-vimeo-component/e63fc4de72316057215b966b795eb25aed04eb28/assets/images/skymap.jpg -------------------------------------------------------------------------------- /assets/js/host-console-warning.js: -------------------------------------------------------------------------------- 1 | var host = window.location.hostname; 2 | 3 | if (host != 'aframe-vimeo-component.glitch.me') { 4 | console.warn("[Vimeo] Remixed the example? don't forget to generate your own token and change the Vimeo video id in the example files. \n Check out our Getting Started guide for more info: \n https://github.com/vimeo/aframe-vimeo-component/wiki/Getting-Started-%F0%9F%9A%80.md") 5 | } -------------------------------------------------------------------------------- /assets/js/readme-glitch-fix.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | require('dotenv').config(); 3 | 4 | var glitchReadMeText = "### Hello and welcome to the Vimeo A-Frame component examples 👋🏼 \n Have a look at our [Getting Started guide](https://github.com/vimeo/aframe-vimeo-component/wiki/Getting-Started-%F0%9F%9A%80), or check out the [full documentation in Github](https://github.com/vimeo/aframe-vimeo-component). \n For questions and support, [open a Github issue](https://github.com/vimeo/aframe-vimeo-component/issues/new)"; 5 | 6 | if (process.env.ENV === 'Glitch') { 7 | console.log('[Ops] Changing the README in Glitch to point to Github documentation'); 8 | 9 | // Change the README to the Glitch one 10 | fs.writeFile('./README.md', glitchReadMeText, 'utf8', function (err) { 11 | if (err) return console.log(err); 12 | }); 13 | } -------------------------------------------------------------------------------- /assets/models/terrain.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vimeo/aframe-vimeo-component/e63fc4de72316057215b966b795eb25aed04eb28/assets/models/terrain.bin -------------------------------------------------------------------------------- /assets/models/terrain.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "accessors" : [ 3 | { 4 | "bufferView" : 0, 5 | "componentType" : 5123, 6 | "count" : 5766, 7 | "max" : [ 8 | 3843 9 | ], 10 | "min" : [ 11 | 0 12 | ], 13 | "type" : "SCALAR" 14 | }, 15 | { 16 | "bufferView" : 1, 17 | "componentType" : 5126, 18 | "count" : 3844, 19 | "max" : [ 20 | 10.0, 21 | 0.3377476930618286, 22 | 10.0 23 | ], 24 | "min" : [ 25 | -10.0, 26 | -0.3428341746330261, 27 | -10.0 28 | ], 29 | "type" : "VEC3" 30 | }, 31 | { 32 | "bufferView" : 2, 33 | "componentType" : 5126, 34 | "count" : 3844, 35 | "max" : [ 36 | 0.48949897289276123, 37 | 0.9999685883522034, 38 | 0.43783536553382874 39 | ], 40 | "min" : [ 41 | -0.4415658116340637, 42 | 0.8394242525100708, 43 | -0.45709797739982605 44 | ], 45 | "type" : "VEC3" 46 | }, 47 | { 48 | "bufferView" : 3, 49 | "componentType" : 5126, 50 | "count" : 3844, 51 | "max" : [ 52 | 0.9999999403953552, 53 | 0.4456324279308319, 54 | 0.005833540111780167, 55 | 1.0 56 | ], 57 | "min" : [ 58 | 0.8719640374183655, 59 | -0.48956984281539917, 60 | -0.005125250201672316, 61 | 1.0 62 | ], 63 | "type" : "VEC4" 64 | }, 65 | { 66 | "bufferView" : 4, 67 | "componentType" : 5126, 68 | "count" : 3844, 69 | "max" : [ 70 | 1.0322575569152832, 71 | 1.0 72 | ], 73 | "min" : [ 74 | 0.0, 75 | 4.76837158203125e-07 76 | ], 77 | "type" : "VEC2" 78 | } 79 | ], 80 | "asset" : { 81 | "generator" : "Khronos Blender glTF 2.0 exporter", 82 | "version" : "2.0" 83 | }, 84 | "bufferViews" : [ 85 | { 86 | "buffer" : 0, 87 | "byteLength" : 11532, 88 | "byteOffset" : 0, 89 | "target" : 34963 90 | }, 91 | { 92 | "buffer" : 0, 93 | "byteLength" : 46128, 94 | "byteOffset" : 11532, 95 | "target" : 34962 96 | }, 97 | { 98 | "buffer" : 0, 99 | "byteLength" : 46128, 100 | "byteOffset" : 57660, 101 | "target" : 34962 102 | }, 103 | { 104 | "buffer" : 0, 105 | "byteLength" : 61504, 106 | "byteOffset" : 103788, 107 | "target" : 34962 108 | }, 109 | { 110 | "buffer" : 0, 111 | "byteLength" : 30752, 112 | "byteOffset" : 165292, 113 | "target" : 34962 114 | } 115 | ], 116 | "buffers" : [ 117 | { 118 | "byteLength" : 196044, 119 | "uri" : "terrain.bin" 120 | } 121 | ], 122 | "meshes" : [ 123 | { 124 | "name" : "Grid", 125 | "primitives" : [ 126 | { 127 | "attributes" : { 128 | "NORMAL" : 2, 129 | "POSITION" : 1, 130 | "TANGENT" : 3, 131 | "TEXCOORD_0" : 4 132 | }, 133 | "indices" : 0 134 | } 135 | ] 136 | } 137 | ], 138 | "nodes" : [ 139 | { 140 | "name" : "Camera", 141 | "rotation" : [ 142 | 0.483536034822464, 143 | 0.33687159419059753, 144 | -0.20870360732078552, 145 | 0.7804827094078064 146 | ], 147 | "translation" : [ 148 | 7.481131553649902, 149 | 5.34366512298584, 150 | 6.5076398849487305 151 | ] 152 | }, 153 | { 154 | "name" : "Lamp", 155 | "rotation" : [ 156 | 0.16907575726509094, 157 | 0.7558802962303162, 158 | -0.27217137813568115, 159 | 0.570947527885437 160 | ], 161 | "scale" : [ 162 | 1.0, 163 | 1.0, 164 | 0.9999999403953552 165 | ], 166 | "translation" : [ 167 | 4.076245307922363, 168 | 5.903861999511719, 169 | -1.0054539442062378 170 | ] 171 | }, 172 | { 173 | "mesh" : 0, 174 | "name" : "Terrain", 175 | "scale" : [ 176 | 1.7770811319351196, 177 | 1.7770811319351196, 178 | 1.7770811319351196 179 | ] 180 | } 181 | ], 182 | "scene" : 0, 183 | "scenes" : [ 184 | { 185 | "name" : "Scene", 186 | "nodes" : [ 187 | 2, 188 | 1, 189 | 0 190 | ] 191 | } 192 | ] 193 | } 194 | -------------------------------------------------------------------------------- /examples/basic.html: -------------------------------------------------------------------------------- 1 | <%- include('header.html'); -%> 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | <%- include('footer.html'); -%> 18 | -------------------------------------------------------------------------------- /examples/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /examples/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vimeo A-Frame Component 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/playback.html: -------------------------------------------------------------------------------- 1 | <%- include('header.html'); -%> 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 41 | 42 | <%- include('footer.html'); -%> -------------------------------------------------------------------------------- /examples/shapes.html: -------------------------------------------------------------------------------- 1 | <%- include('header.html'); -%> 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | <%- include('footer.html'); -%> -------------------------------------------------------------------------------- /examples/webvr-stereo.html: -------------------------------------------------------------------------------- 1 | <%- include('header.html'); -%> 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | <%- include('footer.html'); -%> -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-vimeo-component", 3 | "version": "0.2.0", 4 | "engines": { 5 | "node": ">=0.12" 6 | }, 7 | "dependencies": { 8 | "express": "^4.16.2", 9 | "vimeo": "^1.2.1" 10 | }, 11 | "devDependencies": { 12 | "axios": "^0.17.1", 13 | "dotenv": "^5.0.1", 14 | "ejs": "^2.6.1", 15 | "host-validation": "^2.0.1", 16 | "jsx-loader": "^0.13.2", 17 | "standard": "^12.0.1", 18 | "sync-glitch-cli": "^2.0.1", 19 | "webpack": "^4.12.2", 20 | "webpack-cli": "^3.2.3", 21 | "yargs": "^12.0.5" 22 | }, 23 | "scripts": { 24 | "start": "node assets/js/readme-glitch-fix.js && yarn server", 25 | "server": "node server.js", 26 | "format": "standard ./src/index.js --fix", 27 | "build": "yarn run update:threejs && yarn run build:threejs && yarn run format && yarn build:all", 28 | "build:all": "webpack --env development && webpack --env build", 29 | "build:watch": "webpack --progress --colors --watch --env dev", 30 | "build:threejs": "cd ext/vimeo-threejs-player && yarn && yarn build && cd ../../ && yarn", 31 | "update:threejs": "git submodule update --init --recursive --remote", 32 | "deploy:glitch": "sync-glitch" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const Vimeo = require('vimeo').Vimeo; 2 | const express = require('express'); 3 | const hostValidation = require('host-validation') 4 | const ejs = require('ejs'); 5 | 6 | const app = express(); 7 | 8 | // Render engine for the express server 9 | app.use(express.static('dist')); 10 | app.use(express.static('ext')); 11 | app.use(express.static('assets')); 12 | app.engine('.html', ejs.__express); 13 | app.set('view-engine', 'html'); 14 | app.set('views', __dirname + '/examples'); 15 | 16 | // CORS headers 17 | app.use(function(req, res, next) { 18 | console.log(`[Server] A ${req.method} request was made to ${req.url}`); 19 | res.header('Access-Control-Allow-Origin', '*'); 20 | res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); 21 | next(); 22 | }); 23 | 24 | /* 25 | * Vimeo token for local development is saved in a .env file 26 | * For deployment make sure to store it in an enviorment 27 | * variable called VIMEO_TOKEN=4trwegfudsbg4783724343 28 | */ 29 | if (process.env.NODE_ENV !== 'production') { 30 | require('dotenv').load(); 31 | 32 | if (process.env.VIMEO_TOKEN) { 33 | console.log('[Server] Enviorment variables loaded from .env 💪🏻'); 34 | } else { 35 | console.log('[Server] Could not find a VIMEO_TOKEN. Make sure you have a .env file or enviorment variable with the token'); 36 | } 37 | } 38 | 39 | app.use(hostValidation({ hosts: [`127.0.0.1:${process.env.PORT}`, 40 | `192.168.1.99:${process.env.PORT}`, 41 | `localhost:${process.env.PORT}`, 42 | /.*\.glitch\.com$/, 43 | /.*\.glitch\.me$/, 44 | `${process.env.DOMAIN}:${process.env.PORT}`] })) 45 | 46 | 47 | app.get('/', (request, response) => { 48 | response.render('basic.html'); 49 | }); 50 | 51 | app.get('/basic', (request, response) => { 52 | response.render('basic.html'); 53 | }); 54 | 55 | app.get('/shapes', (request, response) => { 56 | response.render('shapes.html'); 57 | }); 58 | 59 | app.get('/webvr-stereo', (request, response) => { 60 | response.render('webvr-stereo.html'); 61 | }); 62 | 63 | app.get('/playback', (request, response) => { 64 | response.render('playback.html'); 65 | }); 66 | 67 | // The route for getting videos from the vimeo API 68 | app.get('/vimeo/api', (request, response) => { 69 | let api = new Vimeo(null, null, process.env.VIMEO_TOKEN); 70 | 71 | api.request({ 72 | method: 'GET', 73 | path: request.query.path, 74 | headers: { Accept: 'application/vnd.vimeo.*+json;version=3.4' }, 75 | }, 76 | function(error, body, status_code, headers) { 77 | if (error) { 78 | response.status(500).send(error); 79 | console.log('[Server] ' + error); 80 | } else { 81 | // Pass through the whole JSON response 82 | response.status(200).send(body); 83 | } 84 | } 85 | ); 86 | }); 87 | 88 | const listener = app.listen(process.env.PORT, () => { 89 | console.log(`[Server] Running on port: ${listener.address().port} 🚢`); 90 | }); -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /* global THREE, AFRAME */ 2 | const Vimeo = require('./../ext/vimeo-threejs-player/dist/vimeo-threejs-player.min') 3 | 4 | AFRAME.registerComponent('vimeo', { 5 | multiple: true, 6 | schema: { 7 | id: { type: 'number', default: 0 }, 8 | autoplay: { type: 'bool', default: true }, 9 | autoload: { type: 'bool', default: true }, 10 | muted: { type: 'bool', default: false }, 11 | loop: { type: 'bool', default: true }, 12 | volume: { type: 'number', default: 1.0 }, 13 | leftEye: { type: 'selector' }, 14 | rightEye: { type: 'selector' }, 15 | quality: { type: 'string', default: 'auto' } 16 | }, 17 | init: function () { 18 | if (this.data.leftEye && this.data.rightEye) { 19 | this.isStereoSpherical = true 20 | } 21 | 22 | this.player = new Vimeo.Player(this.data.id, { 23 | autoplay: this.data.autoplay, 24 | autoload: this.data.autoload, 25 | muted: this.data.muted, 26 | loop: this.data.loop, 27 | quality: this.data.quality 28 | }) 29 | 30 | // Once the video loads iterate over all objects nested under the and assign the textures 31 | this.player.on('videoLoad', function (videoTexture) { 32 | this.player.setVolume(this.data.volume) 33 | if (this.isStereoSpherical) { 34 | // Create and configure the spheres for stereo webvr 35 | this.createAndAppendStereoSpheresToElement(this.data.leftEye, this.data.rightEye) 36 | this.traverseAndSetVideoTextures(this.data.leftEye.object3D, videoTexture) 37 | this.traverseAndSetVideoTextures(this.data.rightEye.object3D, videoTexture) 38 | } else { 39 | this.traverseAndSetVideoTextures(this.el.object3D, videoTexture) 40 | } 41 | }.bind(this)) 42 | }, 43 | load: function () { 44 | if (this.player) { 45 | this.player.load() 46 | } 47 | }, 48 | play: function () { 49 | this.player.play() 50 | }, 51 | pause: function () { 52 | this.player.pause() 53 | }, 54 | stop: function () { 55 | this.player.stop() 56 | }, 57 | traverseAndSetVideoTextures: function (object, tex) { 58 | object.traverse(function (child) { 59 | if (child instanceof THREE.Mesh) { 60 | child.material.color = null 61 | child.material.map = tex 62 | child.material.needsUpdate = true 63 | } 64 | }) 65 | }, 66 | traverseAndRemoveChildMeshes: function (object) { 67 | object.traverse(function (child) { 68 | if (child instanceof THREE.Mesh) { 69 | object.remove(child) 70 | } 71 | }) 72 | }, 73 | createAndAppendStereoSpheresToElement: function (leftEyeElm, rightEyeElm) { 74 | // If there is pre-existing geomtery in the eye components get rid of it, we will generate it 75 | if (leftEyeElm.object3D.children.length > 0) { 76 | this.traverseAndRemoveChildMeshes(leftEyeElm.object3D) 77 | } 78 | if (rightEyeElm.object3D.children.length > 0) { 79 | this.traverseAndRemoveChildMeshes(rightEyeElm.object3D) 80 | } 81 | 82 | // Build custom geomtery with split UVs for each eye 83 | var geometry = new THREE.SphereBufferGeometry(500, 60, 40) 84 | var material = new THREE.MeshBasicMaterial() 85 | 86 | // Left eye sphere 87 | geometry.scale(-1, 1, 1) 88 | var uvs = geometry.attributes.uv.array 89 | for (var i = 0; i < uvs.length; i += 2) { 90 | uvs[ i + 1 ] *= 0.5 91 | } 92 | var mesh = new THREE.Mesh(geometry, material) 93 | mesh.rotation.y = -Math.PI / 2 94 | leftEyeElm.object3D.add(mesh) 95 | leftEyeElm.object3D.layers.set(1) 96 | 97 | // Right eye sphere 98 | geometry = new THREE.SphereBufferGeometry(500, 60, 40) 99 | geometry.scale(-1, 1, 1) 100 | uvs = geometry.attributes.uv.array 101 | for (var j = 0; j < uvs.length; j += 2) { 102 | uvs[ j + 1 ] *= 0.5 103 | uvs[ j + 1 ] += 0.5 104 | } 105 | mesh = new THREE.Mesh(geometry, material) 106 | mesh.rotation.y = -Math.PI / 2 107 | rightEyeElm.object3D.add(mesh) 108 | rightEyeElm.object3D.layers.set(2) 109 | } 110 | }) 111 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const env = require('yargs').argv.env; // use --env with webpack 2 4 | const pkg = require('./package.json'); 5 | 6 | let libraryName = pkg.name; 7 | 8 | let outputFile, mode; 9 | 10 | if (env === 'build') { 11 | mode = 'production'; 12 | outputFile = libraryName + '.min.js'; 13 | } else { 14 | mode = 'development'; 15 | outputFile = libraryName + '.js'; 16 | } 17 | 18 | module.exports = { 19 | context: path.join(__dirname, './'), 20 | entry: './src/index.js', 21 | mode: mode, 22 | output: { 23 | path: __dirname + '/dist', 24 | filename: outputFile, 25 | library: libraryName, 26 | libraryTarget: 'umd', 27 | umdNamedDefine: true, 28 | globalObject: "typeof self !== 'undefined' ? self : this" 29 | }, 30 | resolve: { 31 | extensions: ['.js', '.jsx'], 32 | }, 33 | module: { 34 | rules: [ 35 | { 36 | test: /\.jsx?$/, 37 | loader: 'jsx-loader', 38 | exclude: /node_modules/, 39 | }, 40 | ], 41 | }, 42 | node: { 43 | fs: 'empty' // fs not available on Glitch 44 | } 45 | }; --------------------------------------------------------------------------------