Skip to content

Commit feba9b8

Browse files
ngokevindmarcos
authored andcommitted
fix userHeight not being applied on mobile (fixes #1780) (#1788)
1 parent b3585e4 commit feba9b8

File tree

2 files changed

+90
-82
lines changed

2 files changed

+90
-82
lines changed

‎src/components/camera.js‎

Lines changed: 40 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,21 @@ module.exports.Component = registerComponent('camera', {
2323
* Add reference from scene to this entity as the camera.
2424
*/
2525
init: function () {
26-
var camera = this.camera = new THREE.PerspectiveCamera();
27-
var sceneEl = this.el.sceneEl;
28-
this.el.setObject3D('camera', camera);
29-
this.bindMethods();
30-
sceneEl.addEventListener('enter-vr', this.removeHeightOffset);
31-
sceneEl.addEventListener('enter-vr', this.saveCameraPose);
32-
sceneEl.addEventListener('exit-vr', this.restoreCameraPose);
33-
sceneEl.addEventListener('exit-vr', this.addHeightOffset);
34-
},
26+
var camera;
27+
var el = this.el;
28+
var sceneEl = el.sceneEl;
29+
30+
this.savedPose = null;
3531

36-
bindMethods: function () {
37-
this.addHeightOffset = this.addHeightOffset.bind(this);
38-
this.removeHeightOffset = this.removeHeightOffset.bind(this);
39-
this.updateHeightOffset = this.updateHeightOffset.bind(this);
40-
this.saveCameraPose = this.saveCameraPose.bind(this);
41-
this.restoreCameraPose = this.restoreCameraPose.bind(this);
32+
// Create camera.
33+
camera = this.camera = new THREE.PerspectiveCamera();
34+
el.setObject3D('camera', camera);
35+
36+
// Add listeners to save and restore camera pose if headset is present.
37+
this.onEnterVR = this.onEnterVR.bind(this);
38+
this.onExitVR = this.onExitVR.bind(this);
39+
sceneEl.addEventListener('enter-vr', this.onEnterVR);
40+
sceneEl.addEventListener('exit-vr', this.onExitVR);
4241
},
4342

4443
/**
@@ -51,10 +50,7 @@ module.exports.Component = registerComponent('camera', {
5150
var system = this.system;
5251

5352
// Update height offset.
54-
if (!oldData || oldData.userHeight !== data.userHeight) {
55-
this.userHeightOffset = oldData.userHeight || 0;
56-
this.updateHeightOffset();
57-
}
53+
this.addHeightOffset(oldData.userHeight);
5854

5955
// Update properties.
6056
camera.aspect = data.aspect || (window.innerWidth / window.innerHeight);
@@ -83,30 +79,40 @@ module.exports.Component = registerComponent('camera', {
8379
remove: function () {
8480
var sceneEl = this.el.sceneEl;
8581
this.el.removeObject3D('camera');
86-
sceneEl.removeEventListener('enter-vr', this.removeHeightOffset);
87-
sceneEl.removeEventListener('enter-vr', this.saveCameraPose);
88-
sceneEl.removeEventListener('exit-vr', this.restoreCameraPose);
89-
sceneEl.removeEventListener('exit-vr', this.addHeightOffset);
82+
sceneEl.removeEventListener('enter-vr', this.onEnterVR);
83+
sceneEl.removeEventListener('exit-vr', this.onExitVR);
84+
},
85+
86+
/**
87+
* Save pose and remove the offset.
88+
*/
89+
onEnterVR: function () {
90+
this.saveCameraPose();
91+
this.removeHeightOffset();
92+
},
93+
94+
/**
95+
* Restore the pose. Do not need to re-add the offset because it was saved on entering VR.
96+
*/
97+
onExitVR: function () {
98+
this.restoreCameraPose();
9099
},
91100

92101
/**
93102
* Offsets the position of the camera to set a human scale perspective
94103
* This offset is not necessary when using a headset because the SDK
95104
* will return the real user's head height and position.
96105
*/
97-
addHeightOffset: function () {
106+
addHeightOffset: function (oldOffset) {
98107
var el = this.el;
99108
var currentPosition;
109+
var userHeightOffset = this.data.userHeight;
100110

101-
// Only applies if there's a default camera with no applied offset.
102-
if (this.userHeightOffset) { return; }
103-
104-
this.userHeightOffset = this.data.userHeight;
105-
111+
oldOffset = oldOffset || 0;
106112
currentPosition = el.getComputedAttribute('position') || {x: 0, y: 0, z: 0};
107113
el.setAttribute('position', {
108114
x: currentPosition.x,
109-
y: currentPosition.y + this.userHeightOffset,
115+
y: currentPosition.y - oldOffset + userHeightOffset,
110116
z: currentPosition.z
111117
});
112118
},
@@ -121,15 +127,14 @@ module.exports.Component = registerComponent('camera', {
121127
var el = this.el;
122128
var headsetConnected;
123129
var sceneEl = el.sceneEl;
124-
var userHeightOffset = this.userHeightOffset;
130+
var userHeightOffset = this.data.userHeight;
125131

126132
// If there's not a headset connected we keep the offset.
127133
// Necessary for fullscreen mode with no headset.
128134
// Checking this.headsetConnected to make the value injectable for unit tests.
129135
headsetConnected = this.headsetConnected || checkHeadsetConnected();
130136
if (sceneEl.isMobile || !userHeightOffset || !headsetConnected) { return; }
131137

132-
this.userHeightOffset = undefined;
133138
currentPosition = el.getAttribute('position') || {x: 0, y: 0, z: 0};
134139
el.setAttribute('position', {
135140
x: currentPosition.x,
@@ -138,29 +143,6 @@ module.exports.Component = registerComponent('camera', {
138143
});
139144
},
140145

141-
/**
142-
* Enables updating camera.userHeight
143-
*/
144-
updateHeightOffset: function () {
145-
var currentPosition;
146-
var headsetConnected;
147-
var el = this.el;
148-
var oldHeightOffset = this.userHeightOffset;
149-
150-
// Do not update camera.userHeight if headset is connected
151-
headsetConnected = this.headsetConnected || checkHeadsetConnected();
152-
if (headsetConnected) { return; }
153-
154-
this.userHeightOffset = this.data.userHeight;
155-
156-
currentPosition = el.getComputedAttribute('position') || {x: 0, y: 0, z: 0};
157-
el.setAttribute('position', {
158-
x: currentPosition.x,
159-
y: currentPosition.y - oldHeightOffset + this.userHeightOffset,
160-
z: currentPosition.z
161-
});
162-
},
163-
164146
/**
165147
* Save camera pose before entering VR to restore later if exiting.
166148
*/
@@ -182,12 +164,13 @@ module.exports.Component = registerComponent('camera', {
182164
restoreCameraPose: function () {
183165
var el = this.el;
184166
var savedPose = this.savedPose;
167+
var headsetConnected = this.headsetConnected || checkHeadsetConnected();
185168

186-
if (!savedPose) { return; }
169+
if (!savedPose || !headsetConnected) { return; }
187170

188171
// Reset camera orientation.
189172
el.setAttribute('position', savedPose.position);
190173
el.setAttribute('rotation', savedPose.rotation);
191-
this.savedPose = undefined;
174+
this.savedPose = null;
192175
}
193176
});

‎tests/components/camera.test.js‎

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,58 +37,70 @@ suite('camera', function () {
3737
el.setAttribute('camera', 'fov: 65');
3838
assert.equal(el.object3D.children[0].uuid, cameraId);
3939
});
40+
41+
test('can update userHeight', function () {
42+
var el = this.el;
43+
assert.shallowDeepEqual(el.getAttribute('position'), {x: 0, y: 1.6, z: 0});
44+
el.setAttribute('camera', 'userHeight', 2.5);
45+
assert.shallowDeepEqual(el.getAttribute('position'), {x: 0, y: 2.5, z: 0});
46+
});
4047
});
4148

42-
suite('restoreCameraPose', function () {
43-
test('restore camera pose when exiting VR with a headset', function () {
44-
var sceneEl = this.el.sceneEl;
49+
suite('saveCameraPose', function () {
50+
test('saves camera pose when entering VR with headset', function () {
4551
var cameraEl = this.el;
52+
var sceneEl = cameraEl.sceneEl;
4653
cameraEl.components.camera.headsetConnected = true;
4754
sceneEl.emit('enter-vr');
48-
cameraEl.setAttribute('position', {x: 6, y: 6, z: 6});
49-
sceneEl.emit('exit-vr');
50-
assert.shallowDeepEqual(cameraEl.getAttribute('position'), {x: 0, y: 1.6, z: 0});
55+
assert.shallowDeepEqual(cameraEl.components.camera.savedPose.position,
56+
{x: 0, y: 1.6, z: 0});
5157
});
5258

53-
test('does not restore camera pose with no headset', function () {
54-
var sceneEl = this.el.sceneEl;
59+
test('does not save camera pose when entering VR without headset', function () {
5560
var cameraEl = this.el;
61+
var sceneEl = cameraEl.sceneEl;
62+
cameraEl.components.camera.headsetConnected = false;
5663
sceneEl.emit('enter-vr');
57-
cameraEl.setAttribute('position', {x: 6, y: 6, z: 6});
58-
sceneEl.emit('exit-vr');
59-
assert.shallowDeepEqual(cameraEl.getAttribute('position'), {x: 6, y: 6, z: 6});
64+
assert.notOk(cameraEl.components.camera.savedPose);
6065
});
6166
});
6267

6368
suite('addHeightOffset', function () {
64-
test('add userHeight offset', function () {
69+
test('adds userHeight offset', function () {
70+
var cameraEl = this.el;
71+
assert.shallowDeepEqual(cameraEl.getAttribute('position'), {x: 0, y: 1.6, z: 0});
72+
});
73+
74+
test('adds userHeight offset on mobile', function () {
6575
var cameraEl = this.el;
76+
var sceneEl = cameraEl.sceneEl;
77+
sceneEl.isMobile = true;
6678
assert.shallowDeepEqual(cameraEl.getAttribute('position'), {x: 0, y: 1.6, z: 0});
6779
});
6880
});
6981

70-
suite('removeHeightOffset', function () {
71-
test('removes the default offset when entering VR with a headset', function () {
72-
var sceneEl = this.el.sceneEl;
82+
suite('removeCameraPose (enter VR)', function () {
83+
test('removes the default offset with headset', function () {
7384
var cameraEl = this.el;
85+
var sceneEl = cameraEl.sceneEl;
7486
cameraEl.components.camera.headsetConnected = true;
7587
assert.shallowDeepEqual(cameraEl.getAttribute('position'), {x: 0, y: 1.6, z: 0});
7688
sceneEl.emit('enter-vr');
7789
assert.shallowDeepEqual(cameraEl.getAttribute('position'), {x: 0, y: 0, z: 0});
7890
});
7991

80-
test('does not remove the default offset when entering VR with no headset', function () {
81-
var sceneEl = this.el.sceneEl;
92+
test('does not remove the default offset without headset', function () {
8293
var cameraEl = this.el;
94+
var sceneEl = cameraEl.sceneEl;
8395
cameraEl.components.camera.headsetConnected = false;
8496
assert.shallowDeepEqual(cameraEl.getAttribute('position'), {x: 0, y: 1.6, z: 0});
8597
sceneEl.emit('enter-vr');
8698
assert.shallowDeepEqual(cameraEl.getAttribute('position'), {x: 0, y: 1.6, z: 0});
8799
});
88100

89-
test('does not remove the default offset when entering VR on mobile', function () {
90-
var sceneEl = this.el.sceneEl;
101+
test('does not remove the default offset on mobile', function () {
91102
var cameraEl = this.el;
103+
var sceneEl = cameraEl.sceneEl;
92104
sceneEl.isMobile = true;
93105
cameraEl.components.camera.headsetConnected = true;
94106
assert.shallowDeepEqual(cameraEl.getAttribute('position'), {x: 0, y: 1.6, z: 0});
@@ -97,12 +109,25 @@ suite('camera', function () {
97109
});
98110
});
99111

100-
suite('updateHeightOffset', function () {
101-
test('updates userHeight', function () {
102-
var el = this.el;
103-
assert.shallowDeepEqual(el.getAttribute('position'), {x: 0, y: 1.6, z: 0});
104-
el.setAttribute('camera', 'userHeight', 2.5);
105-
assert.shallowDeepEqual(el.getAttribute('position'), {x: 0, y: 2.5, z: 0});
112+
suite('restoreCameraPose (exit VR)', function () {
113+
test('restores camera pose with headset', function () {
114+
var cameraEl = this.el;
115+
var sceneEl = cameraEl.sceneEl;
116+
cameraEl.components.camera.headsetConnected = true;
117+
sceneEl.emit('enter-vr');
118+
cameraEl.setAttribute('position', {x: 6, y: 6, z: 6});
119+
sceneEl.emit('exit-vr');
120+
assert.shallowDeepEqual(cameraEl.getAttribute('position'), {x: 0, y: 1.6, z: 0});
121+
});
122+
123+
test('does not restore camera pose without headset', function () {
124+
var sceneEl = this.el.sceneEl;
125+
var cameraEl = this.el;
126+
cameraEl.components.camera.headsetConnected = false;
127+
sceneEl.emit('enter-vr');
128+
cameraEl.setAttribute('position', {x: 6, y: 6, z: 6});
129+
sceneEl.emit('exit-vr');
130+
assert.shallowDeepEqual(cameraEl.getAttribute('position'), {x: 6, y: 6, z: 6});
106131
});
107132
});
108133
});

0 commit comments

Comments
 (0)