├── content-script.js ├── manifest.json └── samples ├── vr-paint-1474965279604.json ├── vr-paint-1474966268338.json ├── vr-paint-1474966394385.json └── vr-sculpt-1474965778952.json /content-script.js: -------------------------------------------------------------------------------- 1 | function main () { 2 | 3 | if ( 'THREE' in window === false ) { console.log( 'VRREPLAY: THREE not found.' ); return; } 4 | if ( 'VRControls' in THREE === false ) { console.log( 'VRREPLAY: THREE.VRControls not found.' ); return; } 5 | if ( 'controls' in window === false ) { console.log( 'VRREPLAY: controls not found.' ); return; } 6 | // if ( 'getVRDisplay' in controls === false ) { console.log( 'VRREC: controls.getVRDisplay() not found.' ); return; } 7 | // if ( 'setVRDisplay' in controls === false ) { console.log( 'VRREC: controls.setVRDisplay() not found.' ); return; } 8 | 9 | if ( 'ViveController' in THREE ) { 10 | 11 | var controller1Available = 'controller1' in window && controller1 instanceof THREE.ViveController; 12 | var controller2Available = 'controller2' in window && controller2 instanceof THREE.ViveController; 13 | 14 | } 15 | 16 | // 17 | 18 | function parseGamepad( gamepad ) { 19 | 20 | if ( gamepad === null ) return null; 21 | 22 | var buttons = gamepad.buttons; 23 | 24 | return { 25 | axes: gamepad.axes, 26 | buttons: [ 27 | { pressed: buttons[ 0 ].pressed, touched: buttons[ 0 ].touched, value: buttons[ 0 ].value }, 28 | { pressed: buttons[ 1 ].pressed, touched: buttons[ 1 ].touched, value: buttons[ 1 ].value }, 29 | { pressed: buttons[ 2 ].pressed, touched: buttons[ 2 ].touched, value: buttons[ 2 ].value }, 30 | { pressed: buttons[ 3 ].pressed, touched: buttons[ 3 ].touched, value: buttons[ 3 ].value } 31 | ], 32 | pose: { 33 | position: Array.from( gamepad.pose.position ), 34 | orientation: Array.from( gamepad.pose.orientation ) 35 | } 36 | }; 37 | 38 | } 39 | 40 | function FakeGamepad() { 41 | 42 | return { 43 | axes: [ 0, 0 ], 44 | buttons: [ 45 | { pressed: false, touched: false, value: 0 }, 46 | { pressed: false, touched: false, value: 0 }, 47 | { pressed: false, touched: false, value: 0 }, 48 | { pressed: false, touched: false, value: 0 } 49 | ], 50 | id: "OpenVR Gamepad", 51 | pose: { position: null, orientation: null } 52 | } 53 | 54 | } 55 | 56 | var gamepads = { 0: new FakeGamepad(), 1: new FakeGamepad(), 2: null, 3: null }; 57 | 58 | var fakeGetGamepads = function () { return gamepads }; 59 | var realGetGamepads = navigator.getGamepads; 60 | 61 | // 62 | 63 | var frames = []; 64 | var currentFrame = 0; 65 | 66 | var isPlaying = false; 67 | var isRecording = false; 68 | 69 | function play() { 70 | 71 | if ( isPlaying === false || frames.length === 0 ) return; 72 | 73 | var frame = frames[ currentFrame ]; 74 | 75 | camera.matrix.fromArray( frame[ 0 ] ); 76 | 77 | if ( controller1Available ) controller1.standingMatrix.fromArray( frame[ 1 ] ); 78 | if ( controller2Available ) controller2.standingMatrix.fromArray( frame[ 1 ] ); 79 | 80 | if ( frame[ 2 ] ) Object.assign( navigator.getGamepads()[ 0 ], frame[ 2 ] ); 81 | if ( frame[ 3 ] ) Object.assign( navigator.getGamepads()[ 1 ], frame[ 3 ] ); 82 | 83 | updateFramesText(); 84 | 85 | currentFrame ++; 86 | 87 | if ( currentFrame >= frames.length ) currentFrame = 0; 88 | 89 | requestAnimationFrame( play ); 90 | 91 | } 92 | 93 | function record() { 94 | 95 | if ( isRecording === false ) return; 96 | 97 | frames.push( [ 98 | camera.matrix.toArray(), 99 | controls.getStandingMatrix().toArray(), 100 | parseGamepad( navigator.getGamepads()[ 0 ] ), 101 | parseGamepad( navigator.getGamepads()[ 1 ] ) 102 | ] ); 103 | 104 | updateFramesText(); 105 | 106 | requestAnimationFrame( record ) 107 | 108 | } 109 | 110 | function updateFramesText() { 111 | 112 | framesText.textContent = currentFrame + '/' + frames.length; 113 | 114 | } 115 | 116 | // 117 | 118 | var panel = document.createElement( 'div' ); 119 | panel.style.position = 'fixed'; 120 | panel.style.top = '0px'; 121 | panel.style.left = '0px'; 122 | panel.style.padding = '8px'; 123 | panel.style.color = 'black'; 124 | panel.style.backgroundColor = 'white'; 125 | panel.innerHTML = 'VR PLAYER - '; 126 | document.body.appendChild( panel ); 127 | 128 | var framesText = document.createTextNode( '0/0' ); 129 | panel.appendChild( framesText ) 130 | 131 | var hr = document.createElement( 'hr' ); 132 | hr.style.cssText = "border: 0px;height: 1px;background-color: #ddd;"; 133 | panel.appendChild( hr ); 134 | 135 | // LOAD 136 | 137 | var fileInput = document.createElement( 'input' ); 138 | fileInput.type = 'file'; 139 | fileInput.addEventListener( 'change', function ( event ) { 140 | 141 | var reader = new FileReader(); 142 | reader.addEventListener( 'load', function ( event ) { 143 | 144 | var contents = event.target.result; 145 | frames = JSON.parse( contents ); 146 | 147 | updateFramesText(); 148 | 149 | }, false ); 150 | reader.readAsText( fileInput.files[ 0 ] ); 151 | 152 | } ); 153 | 154 | var loadButton = document.createElement( 'button' ); 155 | loadButton.textContent = 'LOAD'; 156 | loadButton.addEventListener( 'click', function () { 157 | 158 | fileInput.click(); 159 | 160 | } ); 161 | panel.appendChild( loadButton ); 162 | 163 | // SAVE 164 | 165 | var link = document.createElement( 'a' ); 166 | link.style.display = 'none'; 167 | document.body.appendChild( link ); // Firefox workaround, see #6594 168 | 169 | var saveButton = document.createElement( 'button' ); 170 | saveButton.textContent = 'SAVE'; 171 | saveButton.addEventListener( 'click', function () { 172 | 173 | var text = JSON.stringify( frames ); 174 | 175 | var blob = new Blob( [ text ], { type: 'text/plain' } ); 176 | link.href = URL.createObjectURL( blob ); 177 | link.download = 'vr-' + Date.now() + '.json'; 178 | link.click(); 179 | 180 | } ); 181 | panel.appendChild( saveButton ); 182 | 183 | // CLEAR 184 | 185 | var clearButton = document.createElement( 'button' ); 186 | clearButton.textContent = 'CLEAR'; 187 | clearButton.addEventListener( 'click', function () { 188 | 189 | frames = []; 190 | currentFrame = 0; 191 | updateFramesText(); 192 | 193 | } ); 194 | panel.appendChild( clearButton ); 195 | 196 | // 197 | 198 | var hr = document.createElement( 'hr' ); 199 | hr.style.cssText = "border: 0px;height: 1px;background-color: #ddd;"; 200 | panel.appendChild( hr ); 201 | 202 | // PLAY 203 | 204 | var controlsUpdate = controls.update; 205 | 206 | var playButton = document.createElement( 'button' ); 207 | playButton.textContent = 'PLAY'; 208 | playButton.addEventListener( 'click', function () { 209 | 210 | if ( isPlaying === false ) { 211 | 212 | camera.matrixAutoUpdate = false; 213 | 214 | navigator.getGamepads = fakeGetGamepads; 215 | controls.update = function () {}; 216 | 217 | playButton.textContent = 'STOP'; 218 | 219 | isPlaying = true; 220 | requestAnimationFrame( play ); 221 | 222 | } else { 223 | 224 | camera.matrixAutoUpdate = true; 225 | 226 | navigator.getGamepads = realGetGamepads; 227 | controls.update = controlsUpdate; 228 | 229 | playButton.textContent = 'PLAY'; 230 | 231 | isPlaying = false; 232 | 233 | } 234 | 235 | } ); 236 | panel.appendChild( playButton ); 237 | 238 | // RECORD 239 | 240 | var recordButton = document.createElement( 'button' ); 241 | recordButton.textContent = 'RECORD'; 242 | recordButton.addEventListener( 'click', function () { 243 | 244 | if ( isRecording === false ) { 245 | 246 | recordButton.textContent = 'STOP'; 247 | 248 | isRecording = true; 249 | requestAnimationFrame( record ); 250 | 251 | } else { 252 | 253 | recordButton.textContent = 'RECORD'; 254 | 255 | isRecording = false; 256 | 257 | } 258 | 259 | } ); 260 | panel.appendChild( recordButton ); 261 | 262 | } 263 | 264 | var script = document.createElement('script'); 265 | script.appendChild(document.createTextNode('('+ main +')();')); 266 | (document.body || document.head || document.documentElement).appendChild(script); 267 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Three.js VR Player", 3 | "description": "", 4 | "version": "1.0", 5 | "content_scripts": [ 6 | { 7 | "matches": [""], 8 | "all_frames": true, 9 | "js": ["content-script.js"] 10 | } 11 | ], 12 | "manifest_version": 2 13 | } 14 | --------------------------------------------------------------------------------