Skip to content

Commit ae69e1d

Browse files
machenmusikngokevin
authored andcommitted
add auto-enter-vr scene component that will enterVR() (#2170)
* ad auto-enter-vr component that will enterVR() both with an initial attempt (which may fail, but works on Carmel) and also on vrdisplayactivate (e.g. when you put the HMD on with Chromium) * remove console logs and superfluous code * add auto-enter-vr to scenes by default * only auto-enter on load if GearVR (Carmel) * fix check * added tests and comments per discussion on PR * cleanup per discussion on PR * fix bug preventing enter/exit from really happening * better schema per discussion on PR
1 parent dc45a8c commit ae69e1d

File tree

4 files changed

+223
-1
lines changed

4 files changed

+223
-1
lines changed

src/components/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ require('./visible');
1818
require('./vive-controls');
1919
require('./wasd-controls');
2020

21+
require('./scene/auto-enter-vr');
2122
require('./scene/canvas');
2223
require('./scene/debug');
2324
require('./scene/embedded');
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
var registerComponent = require('../../core/component').registerComponent;
2+
var utils = require('../../utils');
3+
4+
/**
5+
* Automatically enter VR, either upon vrdisplayactivate (e.g. putting on Rift headset)
6+
* or immediately (if possible) if display name contains data string.
7+
* The default data string is 'GearVR' for Carmel browser which only does VR.
8+
*/
9+
module.exports.Component = registerComponent('auto-enter-vr', {
10+
schema: {
11+
display: {type: 'string', default: 'GearVR'},
12+
enabled: {type: 'boolean', default: true}
13+
},
14+
15+
init: function () {
16+
var scene = this.el;
17+
var self = this;
18+
19+
// define methods to allow mock testing
20+
this.enterVR = scene.enterVR.bind(scene);
21+
this.exitVR = scene.exitVR.bind(scene);
22+
this.shouldAutoEnterVR = this.shouldAutoEnterVR.bind(this);
23+
24+
// don't do anything if false
25+
if (utils.getUrlParameter('auto-enter-vr') === 'false') { return; }
26+
27+
// enter VR on vrdisplayactivate (e.g. putting on Rift headset)
28+
window.addEventListener('vrdisplayactivate', function () { self.enterVR(); }, false);
29+
30+
// exit VR on vrdisplaydeactivate (e.g. taking off Rift headset)
31+
window.addEventListener('vrdisplaydeactivate', function () { self.exitVR(); }, false);
32+
33+
// check if we should try to enter VR... turns out we need to wait for next tick
34+
setTimeout(function () { if (self.shouldAutoEnterVR()) { self.enterVR(); } }, 0);
35+
},
36+
37+
update: function () {
38+
return this.shouldAutoEnterVR() ? this.enterVR() : this.exitVR();
39+
},
40+
41+
shouldAutoEnterVR: function () {
42+
var scene = this.el;
43+
var data = this.data;
44+
// if false, we should not auto-enter VR
45+
if (!data.enabled) { return false; }
46+
// if we have a data string to match against display name, try and get it;
47+
// if we can't get display name, or it doesn't match, we should not auto-enter VR
48+
if (data.display) {
49+
var display = scene.effect && scene.effect.getVRDisplay && scene.effect.getVRDisplay();
50+
if (!display || !display.displayName || display.displayName.indexOf(data.display) < 0) { return false; }
51+
}
52+
// we should auto-enter VR
53+
return true;
54+
}
55+
});
56+

src/core/scene/a-scene.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ module.exports = registerElement('a-scene', {
4343
'inspector': '',
4444
'keyboard-shortcuts': '',
4545
'screenshot': '',
46-
'vr-mode-ui': ''
46+
'vr-mode-ui': '',
47+
'auto-enter-vr': ''
4748
}
4849
},
4950

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
/* global assert, process, setup, suite, test */
2+
var entityFactory = require('../../helpers').entityFactory;
3+
4+
suite('auto-enter-vr', function () {
5+
suite('no getVRDisplay', function () {
6+
setup(function (done) {
7+
this.entityEl = entityFactory();
8+
var el = this.el = this.entityEl.parentNode;
9+
var resolvePromise = function () { return Promise.resolve(); };
10+
el.setAttribute('auto-enter-vr', '');
11+
el.effect = {
12+
requestPresent: resolvePromise,
13+
exitPresent: resolvePromise
14+
};
15+
var self = this;
16+
var autoEnterVR = el.components['auto-enter-vr'];
17+
el.addEventListener('loaded', function () {
18+
self.enterVRSpy = self.sinon.spy(autoEnterVR, 'enterVR');
19+
self.exitVRSpy = self.sinon.spy(autoEnterVR, 'exitVR');
20+
done();
21+
});
22+
});
23+
24+
test('if no getVRDisplay, then !shouldAutoEnterVR and !enterVR', function () {
25+
var el = this.el;
26+
var autoEnterVR = el.components['auto-enter-vr'];
27+
assert.notOk(el.effect.getVRDisplay);
28+
assert.notOk(autoEnterVR.shouldAutoEnterVR());
29+
process.nextTick(function () {
30+
assert.notOk(this.enterVRSpy.called);
31+
});
32+
});
33+
});
34+
35+
suite('!getVRDisplay()', function () {
36+
setup(function (done) {
37+
this.entityEl = entityFactory();
38+
var el = this.el = this.entityEl.parentNode;
39+
var resolvePromise = function () { return Promise.resolve(); };
40+
el.setAttribute('auto-enter-vr', '');
41+
el.effect = {
42+
getVRDisplay: function () { return null; },
43+
requestPresent: resolvePromise,
44+
exitPresent: resolvePromise
45+
};
46+
var self = this;
47+
var autoEnterVR = el.components['auto-enter-vr'];
48+
el.addEventListener('loaded', function () {
49+
self.enterVRSpy = self.sinon.spy(autoEnterVR, 'enterVR');
50+
self.exitVRSpy = self.sinon.spy(autoEnterVR, 'exitVR');
51+
done();
52+
});
53+
});
54+
55+
test('if !getVRDisplay(), then !shouldAutoEnterVR and !enterVR', function () {
56+
var el = this.el;
57+
var autoEnterVR = el.components['auto-enter-vr'];
58+
assert.ok(el.effect.getVRDisplay);
59+
assert.notOk(el.effect.getVRDisplay());
60+
assert.notOk(autoEnterVR.shouldAutoEnterVR());
61+
process.nextTick(function () {
62+
assert.notOk(this.enterVRSpy.called);
63+
});
64+
});
65+
});
66+
67+
suite('no getVRDisplay() displayName', function () {
68+
setup(function (done) {
69+
this.entityEl = entityFactory();
70+
var el = this.el = this.entityEl.parentNode;
71+
var resolvePromise = function () { return Promise.resolve(); };
72+
el.setAttribute('auto-enter-vr', '');
73+
el.effect = {
74+
getVRDisplay: function () { return {}; },
75+
requestPresent: resolvePromise,
76+
exitPresent: resolvePromise
77+
};
78+
var self = this;
79+
var autoEnterVR = el.components['auto-enter-vr'];
80+
el.addEventListener('loaded', function () {
81+
self.enterVRSpy = self.sinon.spy(autoEnterVR, 'enterVR');
82+
self.exitVRSpy = self.sinon.spy(autoEnterVR, 'exitVR');
83+
done();
84+
});
85+
});
86+
87+
test('if no getVRDisplay().displayName, then !shouldAutoEnterVR and !enterVR', function () {
88+
var el = this.el;
89+
var autoEnterVR = el.components['auto-enter-vr'];
90+
assert.ok(el.effect.getVRDisplay);
91+
assert.notOk(el.effect.getVRDisplay().displayName);
92+
assert.notOk(autoEnterVR.shouldAutoEnterVR());
93+
process.nextTick(function () {
94+
assert.notOk(this.enterVRSpy.called);
95+
});
96+
});
97+
});
98+
99+
suite('getVRDisplay() displayName something else', function () {
100+
setup(function (done) {
101+
this.entityEl = entityFactory();
102+
var el = this.el = this.entityEl.parentNode;
103+
var resolvePromise = function () { return Promise.resolve(); };
104+
el.setAttribute('auto-enter-vr', '');
105+
el.effect = {
106+
getVRDisplay: function () { return {displayName: 'something else'}; },
107+
requestPresent: resolvePromise,
108+
exitPresent: resolvePromise
109+
};
110+
var self = this;
111+
var autoEnterVR = el.components['auto-enter-vr'];
112+
el.addEventListener('loaded', function () {
113+
self.enterVRSpy = self.sinon.spy(autoEnterVR, 'enterVR');
114+
self.exitVRSpy = self.sinon.spy(autoEnterVR, 'exitVR');
115+
done();
116+
});
117+
});
118+
119+
test('if getVRDisplay().displayName something else, then !shouldAutoEnterVR and !enterVR', function () {
120+
var el = this.el;
121+
var autoEnterVR = el.components['auto-enter-vr'];
122+
assert.ok(el.effect.getVRDisplay);
123+
assert.ok(el.effect.getVRDisplay().displayName);
124+
assert.notOk(el.effect.getVRDisplay().displayName.indexOf('GearVR') >= 0);
125+
assert.notOk(autoEnterVR.shouldAutoEnterVR());
126+
process.nextTick(function () {
127+
assert.notOk(this.enterVRSpy.called);
128+
});
129+
});
130+
});
131+
132+
suite('getVRDisplay() displayName GearVR something', function () {
133+
setup(function (done) {
134+
this.entityEl = entityFactory();
135+
var el = this.el = this.entityEl.parentNode;
136+
var resolvePromise = function () { return Promise.resolve(); };
137+
el.setAttribute('auto-enter-vr', '');
138+
el.effect = {
139+
getVRDisplay: function () { return {displayName: 'GearVR something'}; },
140+
requestPresent: resolvePromise,
141+
exitPresent: resolvePromise
142+
};
143+
var self = this;
144+
var autoEnterVR = el.components['auto-enter-vr'];
145+
el.addEventListener('loaded', function () {
146+
self.enterVRSpy = self.sinon.spy(autoEnterVR, 'enterVR');
147+
self.exitVRSpy = self.sinon.spy(autoEnterVR, 'exitVR');
148+
done();
149+
});
150+
});
151+
152+
test('if getVRDisplay().displayName has GearVR, then shouldAutoEnterVR and enterVR', function () {
153+
var el = this.el;
154+
var autoEnterVR = el.components['auto-enter-vr'];
155+
assert.ok(el.effect.getVRDisplay);
156+
assert.ok(el.effect.getVRDisplay().displayName);
157+
assert.ok(el.effect.getVRDisplay().displayName.indexOf('GearVR') >= 0);
158+
assert.ok(autoEnterVR.shouldAutoEnterVR());
159+
process.nextTick(function () {
160+
assert.ok(this.enterVRSpy.called);
161+
});
162+
});
163+
});
164+
});

0 commit comments

Comments
 (0)