├── about ├── icon.png ├── capture.jpg ├── small-tile.jpg ├── snapshot1.jpg ├── snapshot2.jpg ├── snapshot1-1280x800.jpg ├── snapshot2-1280x800.jpg └── ChromeWebStore_Badge_v2_496x150.png ├── devtools.html ├── assets ├── headset-ao.png ├── chaperone-texture.png ├── save.svg ├── local.svg ├── delete.svg ├── reset1.svg ├── eye.svg ├── reset2.svg ├── translate.svg ├── activate.svg ├── sitting.svg ├── rotate.svg ├── world.svg └── headset.obj ├── archive ├── Archive-1.0.5.zip ├── Archive-1.1.0.zip ├── Archive-1.1.1.zip ├── Archive-1.1.2.zip ├── Archive-1.1.3.zip └── Archive-1.1.4.zip ├── manifest.json ├── utils.js ├── LICENSE ├── common.js ├── README.md ├── devtools.js ├── content-script.js ├── CHANGELOG.md ├── background.js ├── panel.html ├── panel.js ├── polyfill.js ├── OBJLoader.js ├── OrbitControls.js └── TransformControls.js /about/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/WebVR-Extension/HEAD/about/icon.png -------------------------------------------------------------------------------- /about/capture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/WebVR-Extension/HEAD/about/capture.jpg -------------------------------------------------------------------------------- /about/small-tile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/WebVR-Extension/HEAD/about/small-tile.jpg -------------------------------------------------------------------------------- /about/snapshot1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/WebVR-Extension/HEAD/about/snapshot1.jpg -------------------------------------------------------------------------------- /about/snapshot2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/WebVR-Extension/HEAD/about/snapshot2.jpg -------------------------------------------------------------------------------- /devtools.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/headset-ao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/WebVR-Extension/HEAD/assets/headset-ao.png -------------------------------------------------------------------------------- /archive/Archive-1.0.5.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/WebVR-Extension/HEAD/archive/Archive-1.0.5.zip -------------------------------------------------------------------------------- /archive/Archive-1.1.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/WebVR-Extension/HEAD/archive/Archive-1.1.0.zip -------------------------------------------------------------------------------- /archive/Archive-1.1.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/WebVR-Extension/HEAD/archive/Archive-1.1.1.zip -------------------------------------------------------------------------------- /archive/Archive-1.1.2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/WebVR-Extension/HEAD/archive/Archive-1.1.2.zip -------------------------------------------------------------------------------- /archive/Archive-1.1.3.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/WebVR-Extension/HEAD/archive/Archive-1.1.3.zip -------------------------------------------------------------------------------- /archive/Archive-1.1.4.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/WebVR-Extension/HEAD/archive/Archive-1.1.4.zip -------------------------------------------------------------------------------- /about/snapshot1-1280x800.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/WebVR-Extension/HEAD/about/snapshot1-1280x800.jpg -------------------------------------------------------------------------------- /about/snapshot2-1280x800.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/WebVR-Extension/HEAD/about/snapshot2-1280x800.jpg -------------------------------------------------------------------------------- /assets/chaperone-texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/WebVR-Extension/HEAD/assets/chaperone-texture.png -------------------------------------------------------------------------------- /about/ChromeWebStore_Badge_v2_496x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/WebVR-Extension/HEAD/about/ChromeWebStore_Badge_v2_496x150.png -------------------------------------------------------------------------------- /assets/save.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/local.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/delete.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/reset1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WebVR API Emulation", 3 | "short_name": "WebVR Emu", 4 | "version": "1.1.4", 5 | "minimum_chrome_version": "10.0", 6 | "devtools_page": "devtools.html", 7 | "description": "WebVR API Emulation", 8 | "background": { 9 | "scripts": [ "common.js", "background.js" ] 10 | }, 11 | "permissions": [ 12 | "tabs", 13 | "
6 |
7 | It can also help develop even if one owns an HMD, by making repetitive actions that require putting the HMD on and off easier to check through the DevTools panel.
8 |
9 | 
10 |
11 | [Here's a video showing it in action](https://www.youtube.com/watch?v=D8dj0bk3cWQ)
12 |
13 | 
14 | 
15 |
16 | **This extension completely polyfills the WebVR API.
17 | If you have a working WebVR implementation, it will be replaced!**
18 |
19 | This is a work in progress in a very early stage.
20 | It can't be turned off except by disabling or removing the extension
21 |
22 | #### License ####
23 |
24 | MIT licensed
25 |
26 | Copyright (C) 2016 Jaume Sanchez Elias, http://www.clicktorelease.com
27 |
--------------------------------------------------------------------------------
/devtools.js:
--------------------------------------------------------------------------------
1 | var verbose = false;
2 |
3 | chrome.devtools.panels.create( "WebVR", "icon.png", "panel.html", initialise );
4 |
5 | function log( ...args ) {
6 |
7 | if( !verbose ) return;
8 |
9 | var strArgs = [
10 | '"%c WebVREmu | DevTools "',
11 | '"background: #007AA3; color: #ffffff; text-shadow: 0 -1px #000; padding: 4px 0 4px 0; line-height: 0;"',
12 | ...args.map( v => JSON.stringify( v ) )
13 | ];
14 |
15 | chrome.devtools.inspectedWindow.eval(
16 | `console.log(${strArgs});`,
17 | ( result, isException ) => { if( isException ) { console.log( result, isException ) } }
18 | );
19 |
20 | }
21 |
22 | log( 'Ready' );
23 |
24 | var port = chrome.runtime.connect( null, { name: `devtools` } );
25 | var tabId = chrome.devtools.inspectedWindow.tabId
26 |
27 | function post( msg ) {
28 |
29 | msg.tabId = tabId;
30 | port.postMessage( msg );
31 |
32 | }
33 |
34 | post( { action: 'start' } );
35 |
36 | port.onDisconnect.addListener( function() {
37 | console.log( 'disconnect' );
38 | } );
39 |
40 | var settings = {};
41 | var panelWindow = null;
42 |
43 | port.onMessage.addListener( function( msg ) {
44 |
45 | switch( msg.action ) {
46 | case 'settings':
47 | settings = msg.settings;
48 | if( panelWindow ) panelWindow.updateSettings( settings );
49 | break;
50 | case 'pose':
51 | if( panelWindow ) panelWindow.updatePose( msg.position, msg.rotation );
52 | break;
53 | }
54 |
55 | } );
56 |
57 | function initialise( panel ) {
58 |
59 | panel.onShown.addListener( function ( wnd ) {
60 |
61 | panelWindow = wnd;
62 | panelWindow.updateSettings( settings );
63 |
64 | } );
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/assets/rotate.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/content-script.js:
--------------------------------------------------------------------------------
1 | var verbose = true;
2 |
3 | function log() {
4 |
5 | if( !verbose ) return;
6 |
7 | console.log.apply(
8 | console, [
9 | `%c WebVREmu `,
10 | 'background: #007AA3; color: #ffffff; text-shadow: 0 -1px #000; padding: 4px 0 4px 0; line-height: 0',
11 | ...arguments
12 | ]
13 | );
14 |
15 | }
16 |
17 | log( 'Polyfill', window.location.toString() );
18 |
19 | var port = chrome.runtime.connect( { name: 'contentScript' } );
20 |
21 | function post( msg ) {
22 |
23 | port.postMessage( msg );
24 |
25 | }
26 |
27 | post( { action: 'script-ready' } );
28 |
29 | var cloneInto = cloneInto || null;
30 |
31 | function createCustomEvent(type, detail) {
32 | // Use cloneInto on Firefox (which prevents the security
33 | // sandbox from raising exception when the javascript
34 | // code in the webpage content is going to access to the
35 | // event properties).
36 | detail = cloneInto ? cloneInto(detail, window) : detail;
37 | return new CustomEvent( type, { detail });
38 | }
39 |
40 | port.onMessage.addListener( function( msg ) {
41 |
42 | switch( msg.action ) {
43 | case 'pose':
44 | var e = createCustomEvent( 'webvr-pose', {
45 | position: msg.position,
46 | rotation: msg.rotation
47 | } );
48 | window.dispatchEvent( e );
49 | break;
50 | case 'hmd-activate':
51 | var e = createCustomEvent( 'webvr-hmd-activate', {
52 | state: msg.value
53 | } );
54 | window.dispatchEvent( e );
55 | break;
56 | }
57 |
58 | } );
59 |
60 | window.addEventListener( 'webvr-ready', function() {
61 |
62 | post( { action: 'page-ready' } );
63 |
64 | } );
65 |
66 | window.addEventListener( 'webvr-resetpose', function() {
67 |
68 | post( { action: 'reset-pose' } );
69 |
70 | } );
71 |
72 | var source = '(' + injectedScript + ')();';
73 |
74 | var script = document.createElement('script');
75 | script.textContent = source;
76 | (document.head||document.documentElement).appendChild(script);
77 | script.parentNode.removeChild(script);
78 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 | For "Chrome DevTools extension to emulate WebVR API"
3 | (Added, Changed, Deprecated, Removed, Fixed, Security)
4 | https://github.com/spite/WebVR-Extension
5 |
6 | ## [Unreleased]
7 |
8 | ## [1.1.4] - 2016-11-11
9 | ### Added
10 | - Support for VRFrameData and VRDisplay.getFrameData by @spite (see https://github.com/spite/WebVR-Extension/issues/21)
11 |
12 | ## [1.1.3] - 2016-11-2
13 | ### Fixed
14 | - VRLayers handling by @spite
15 |
16 | ## [1.1.2] - 2016-10-28
17 | ### Added
18 | - Support for VRDisplay.getLayers() by @AVGP
19 |
20 | ## [1.1.1] - 2016-09-25
21 | ### Added
22 | - Activate/deactivate HMD feature by @spite
23 | - VRDisplay.getLayers
24 |
25 | ### Fixed
26 | - vrdisplay and reason in VRDisplayEvent by @spite
27 |
28 | ## [1.1.0] - 2016-09-13
29 | ### Added
30 | - Options for persistence on reload by @spite
31 | - Options for sharing pose across tabs by @spite
32 | - Store and restore poses by @spite
33 | - Added link to repo and changelog by @spite
34 | - Action buttons on the bottom bar by @spite
35 |
36 | ### Changed
37 | - Switched message passing to CustomEvent dispatching by @spite
38 | - Revamped UI and styles by @spite
39 | - Snapping translation units from 1m to 10cm by @spite
40 |
41 | ### Fixed
42 | - TransformControls with OrthographicCamera by @mrdoob
43 | - Emulated HMDs had externalDisplay disabled by @spite
44 | - Ctrl key release by @spite
45 |
46 | ## [1.0.5] - 2016-08-30
47 | ### Added
48 | - cancelAnimationFrame by @johnmaf
49 |
50 | ### Fixed
51 | - Populate VRDisplay's righteye offset from model's right eye offset by @johnmaf
52 | - Return value for requestAnimationFrame by @johnmaf
53 |
54 | ## [1.0.4] - 2016-08-30
55 | ### Fixed
56 | - Fixed default quaternions by @spite
57 |
58 | ## [1.0.3] - 2016-07-12
59 | ### Added
60 | - Emit event vrdisplaypresentchange by @spite
61 | - VRDisplay.exitPresent by @spite
62 |
63 | ### Changed
64 | - Removed Float32Array allocation in VRDisplay.getPose by @spite
65 | - Profiles for Vive, Rift and Cardboard by @spite
66 |
67 | ### Greetings
68 | - @edankwan for helping debug issue
69 |
70 | ## [1.0.2] - 2016-07-08
71 | ### Added
72 | - Support for iframes by @spite
73 | - Initial Polyfill for legacy API by @spite
74 |
75 | ## [1.0.1] - 2016-07-1
76 | ### Fixed
77 | - Drawing order by @mrdoob.
78 |
79 | ### Changed
80 | - Camera position by @mrdoob.
81 | - Default StageParameters dimensions by @spite
82 | - Changes to TransformControls by @mrdoob.
83 |
84 | ### Added
85 | - Change log
86 |
87 | ## [1.0.0] - 2016-06-30
88 | ### Added
89 | - Everything. First release to the Chrome Store
90 |
--------------------------------------------------------------------------------
/background.js:
--------------------------------------------------------------------------------
1 | var verbose = false;
2 |
3 | var extensionId = chrome.runtime.id;
4 | log( 'Background', extensionId );
5 |
6 | var settings = {};
7 | var script = '';
8 |
9 | var defaultSettings = {
10 |
11 | persist: true,
12 | individualPose: true,
13 | poses: [],
14 | poseCount: 0
15 |
16 | }
17 |
18 | var defaultPose = {
19 | position: { x: 0, y: 0, z: 0 },
20 | rotation: { x: 0, y: 0, z: 0, w: 1 }
21 | };
22 | var globalPose = {
23 | position: { x: 0, y: 0, z: 0 },
24 | rotation: { x: 0, y: 0, z: 0, w: 1 }
25 | };
26 | var poses = {};
27 |
28 | loadSettings().then( res => {
29 | settings = res;
30 | notifySettings();
31 | log( 'Script and settings loaded', settings );
32 | } );
33 |
34 | function notifySettings() {
35 |
36 | Object.keys( connections ).forEach( tab => {
37 | if( connections[ tab ].devtools ) {
38 | connections[ tab ].devtools.postMessage( {
39 | action: 'settings',
40 | settings: settings
41 | } )
42 | }
43 | } );
44 |
45 | }
46 |
47 | function notifyPoseToInstance( pose, tabId ) {
48 |
49 | var msg = {
50 | action: 'pose',
51 | position: pose.position,
52 | rotation: pose.rotation
53 | };
54 |
55 | // there should always be at least a content script
56 | // but may be the extension has been reloaded
57 |
58 | if( connections[ tabId ].contentScript) {
59 | connections[ tabId ].contentScript.postMessage( msg );
60 | }
61 |
62 | if( connections[ tabId ].devtools) {
63 | connections[ tabId ].devtools.postMessage( msg );
64 | }
65 |
66 | }
67 |
68 | function notifyPose( tabId ) {
69 |
70 | if( settings.individualPose ) {
71 |
72 | notifyPoseToInstance( poses[ tabId ], tabId );
73 |
74 | } else {
75 |
76 | Object.keys( connections ).forEach( tab => {
77 | notifyPoseToInstance( globalPose, tab );
78 | } );
79 |
80 | }
81 |
82 | }
83 |
84 | var connections = {};
85 |
86 | chrome.runtime.onConnect.addListener( function( port ) {
87 |
88 | log( 'New connection (chrome.runtime.onConnect) from', port.name, port.sender.frameId, port );
89 |
90 | var name = port.name;
91 |
92 | if( name === 'devtools' ){
93 | port.postMessage( {
94 | action: 'settings',
95 | settings: settings
96 | } );
97 | }
98 |
99 | function listener( msg, sender, reply ) {
100 |
101 | var tabId;
102 |
103 | if( msg.tabId ) tabId = msg.tabId
104 | else tabId = sender.sender.tab.id;
105 |
106 | if( !connections[ tabId ] ) connections[ tabId ] = {};
107 | connections[ tabId ][ name ] = port;
108 |
109 | if( !poses[ tabId ] ) {
110 | poses[ tabId ] = defaultPose;
111 | }
112 |
113 | if( name === 'panel' ) {
114 | switch( msg.action ) {
115 | case 'save-settings':
116 | settings = msg.settings;
117 | saveSettings( msg.settings );
118 | notifySettings();
119 | break;
120 | case 'save-pose':
121 | settings.poseCount++;
122 | settings.poses.push( {
123 | name: `Pose ${settings.poseCount}`,
124 | position: msg.pose.position,
125 | rotation: msg.pose.rotation
126 | } );
127 | saveSettings( settings );
128 | notifySettings();
129 | break;
130 | case 'remove-pose':
131 | settings.poses.splice( msg.id, 1 );
132 | saveSettings( settings );
133 | notifySettings();
134 | break;
135 | case 'pose':
136 | globalPose.position = msg.position;
137 | globalPose.rotation = msg.rotation;
138 | poses[ tabId ] = {
139 | position: msg.position,
140 | rotation: msg.rotation
141 | };
142 | notifyPose( tabId );
143 | break;
144 | case 'hmd-activate':
145 | connections[ tabId ].contentScript.postMessage( { action: 'hmd-activate', value: msg.value } );
146 | break;
147 | }
148 | }
149 |
150 | if( name === 'contentScript' ) {
151 | if( msg.action === 'page-ready' ) {
152 | if( !settings.persist ) {
153 | poses[ tabId ] = defaultPose;
154 | }
155 | notifyPose( tabId );
156 | }
157 | }
158 |
159 | if( msg.action === 'reset-pose' ) {
160 | globalPose.position = defaultPose.position;
161 | globalPose.rotation = defaultPose.rotation;
162 | poses[ tabId ] = defaultPose;
163 | notifyPose( tabId );
164 | }
165 |
166 | log( 'port.onMessage', port.name, msg );
167 |
168 | }
169 |
170 | port.onMessage.addListener( listener );
171 |
172 | port.onDisconnect.addListener( function() {
173 |
174 | port.onMessage.removeListener( listener );
175 |
176 | log( name, 'disconnect (chrome.runtime.onDisconnect)' );
177 |
178 | Object.keys( connections ).forEach( c => {
179 | if( connections[ c ][ name ] === port ) {
180 | connections[ c ][ name ] = null;
181 | delete connections[ c ][ name ];
182 | }
183 | if ( Object.keys( connections[ c ] ).length === 0 ) {
184 | connections[ c ] = null;
185 | delete connections[ c ];
186 | }
187 | } )
188 |
189 |
190 | } );
191 |
192 | port.postMessage( { action: 'ack' } );
193 |
194 | return true;
195 |
196 | });
197 |
--------------------------------------------------------------------------------
/panel.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | Click for Disclaimer and instructions
50 |Camera controls
Left-click and drag on screen to rotate - Right-click to pan - Mousewheel to zoom
Transform controls
W Translate - E Rotate - Q to switch between Local and World space
Ctrl Snap translation to 10cm (4") and rotation to 15º
Reset pose to simulate a resetPose (HMD goes back to origin)
55 | Save pose to store the current pose and use it later
56 | Selected saved Pose to retrieve a previously saved pose
57 | Reset room to reset the transformation in the room viewer
Warning: this extension completely polyfills the WebVR API. If you have a working WebVR implementation, it will be replaced! This is a work in progress in a very early stage, can't be turned off except by disabling or removing the extension
61 |
70 |
71 |
72 |
73 |
76 | 77 | 78 |
Position 0 0 0
Orientation 0 0 0 0