Skip to content

Commit 9687531

Browse files
ngokevindmarcos
authored andcommitted
raycaster fixes and perf improvements (fixes #3428, #3429) (#3250)
* remove object allocations from cursor component * only emit raycaster intersection/intersected events if newly intersected * fix disabling of raycaster.showLine * fix disabling raycaster.line getting overridden by tick line updater * remove arrow fn
1 parent 931210e commit 9687531

File tree

3 files changed

+116
-51
lines changed

3 files changed

+116
-51
lines changed

‎src/components/cursor.js‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ module.exports.Component = registerComponent('cursor', {
6161
self.canvasBounds = self.el.sceneEl.canvas.getBoundingClientRect();
6262
}, 200);
6363

64+
this.eventDetail = {};
65+
this.intersectedEventDetail = {cursorEl: this.el};
66+
6467
// Bind methods.
6568
this.onCursorDown = bind(this.onCursorDown, this);
6669
this.onCursorUp = bind(this.onCursorUp, this);
@@ -235,7 +238,8 @@ module.exports.Component = registerComponent('cursor', {
235238
// If intersected entity has changed since the cursorDown, still emit mouseUp on the
236239
// previously cursorUp entity.
237240
if (this.cursorDownEl && this.cursorDownEl !== this.intersectedEl) {
238-
this.cursorDownEl.emit(EVENTS.MOUSEUP, {cursorEl: this.el, intersection: null});
241+
this.intersectedEventDetail.intersection = null;
242+
this.cursorDownEl.emit(EVENTS.MOUSEUP, this.intersectedEventDetail);
239243
}
240244

241245
if (!this.data.fuse && this.intersectedEl && this.cursorDownEl === this.intersectedEl) {
@@ -329,8 +333,14 @@ module.exports.Component = registerComponent('cursor', {
329333
var el = this.el;
330334
var intersectedEl = this.intersectedEl;
331335
var intersection = this.intersection;
332-
el.emit(evtName, {intersectedEl: intersectedEl, intersection: intersection});
336+
337+
this.eventDetail.intersectedEl = intersectedEl;
338+
this.eventDetail.intersection = intersection;
339+
el.emit(evtName, this.eventDetail);
340+
333341
if (!intersectedEl) { return; }
334-
intersectedEl.emit(evtName, {cursorEl: el, intersection: intersection});
342+
343+
this.intersectedEventDetail.intersection = intersection;
344+
intersectedEl.emit(evtName, this.intersectedEventDetail);
335345
}
336346
});

‎src/components/raycaster.js‎

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ module.exports.Component = registerComponent('raycaster', {
5353
this.clearedIntersectedEls = [];
5454
this.unitLineEndVec3 = new THREE.Vector3();
5555
this.intersectedEls = [];
56+
this.newIntersectedEls = [];
57+
this.newIntersections = [];
5658
this.objects = [];
5759
this.prevCheckTime = undefined;
5860
this.prevIntersectedEls = [];
@@ -61,10 +63,12 @@ module.exports.Component = registerComponent('raycaster', {
6163
this.setDirty = this.setDirty.bind(this);
6264
this.observer = new MutationObserver(this.setDirty);
6365
this.dirty = true;
64-
this.intersectionClearedDetail = {clearedEls: this.clearedIntersectedEls};
6566
this.lineEndVec3 = new THREE.Vector3();
6667
this.otherLineEndVec3 = new THREE.Vector3();
6768
this.lineData = {end: this.lineEndVec3};
69+
70+
this.intersectedClearedDetail = {el: this.el};
71+
this.intersectionClearedDetail = {clearedEls: this.clearedIntersectedEls};
6872
},
6973

7074
/**
@@ -82,10 +86,11 @@ module.exports.Component = registerComponent('raycaster', {
8286
// Draw line.
8387
if (data.showLine &&
8488
(data.far !== oldData.far || data.origin !== oldData.origin ||
85-
data.direction !== oldData.direction || data.showLine !== oldData.showLine)) {
89+
data.direction !== oldData.direction || !oldData.showLine)) {
8690
this.unitLineEndVec3.copy(data.origin).add(data.direction).normalize();
8791
this.drawLine();
8892
}
93+
8994
if (!data.showLine && oldData.showLine) {
9095
el.removeAttribute('line');
9196
}
@@ -179,8 +184,11 @@ module.exports.Component = registerComponent('raycaster', {
179184
var intersectedEls = this.intersectedEls;
180185
var intersection;
181186
var lineLength;
187+
var newIntersectedEls = this.newIntersectedEls;
188+
var newIntersections = this.newIntersections;
182189
var prevIntersectedEls = this.prevIntersectedEls;
183190
var rawIntersections;
191+
var self = this;
184192

185193
if (!this.data.enabled) { return; }
186194

@@ -196,6 +204,7 @@ module.exports.Component = registerComponent('raycaster', {
196204

197205
// Only keep intersections against objects that have a reference to an entity.
198206
intersections.length = 0;
207+
intersectedEls.length = 0;
199208
for (i = 0; i < rawIntersections.length; i++) {
200209
intersection = rawIntersections[i];
201210
// Don't intersect with own line.
@@ -204,53 +213,61 @@ module.exports.Component = registerComponent('raycaster', {
204213
}
205214
if (intersection.object.el) {
206215
intersections.push(intersection);
216+
intersectedEls.push(intersection.object.el);
207217
}
208218
}
209219

210-
// Update intersectedEls.
211-
intersectedEls.length = intersections.length;
212-
for (i = 0; i < intersections.length; i++) {
213-
intersectedEls[i] = intersections[i].object.el;
214-
}
215-
216-
// Emit intersected on intersected entity per intersected entity.
220+
// Get newly intersected entities.
221+
newIntersections.length = 0;
222+
newIntersectedEls.length = 0;
217223
for (i = 0; i < intersections.length; i++) {
218-
intersections[i].object.el.emit('raycaster-intersected', {
219-
el: el,
220-
intersection: intersections[i]
221-
});
222-
}
223-
224-
// Emit all intersections at once on raycasting entity.
225-
if (intersections.length) {
226-
el.emit('raycaster-intersection', {
227-
els: intersectedEls,
228-
intersections: intersections
229-
});
224+
if (prevIntersectedEls.indexOf(intersections[i].object.el) === -1) {
225+
newIntersections.push(intersections[i]);
226+
newIntersectedEls.push(intersections[i].object.el);
227+
}
230228
}
231229

232230
// Emit intersection cleared on both entities per formerly intersected entity.
233231
clearedIntersectedEls.length = 0;
234232
for (i = 0; i < prevIntersectedEls.length; i++) {
235233
if (intersectedEls.indexOf(prevIntersectedEls[i]) !== -1) { continue; }
236-
prevIntersectedEls[i].emit('raycaster-intersected-cleared', {el: el});
234+
prevIntersectedEls[i].emit('raycaster-intersected-cleared',
235+
this.intersectedClearedDetail);
237236
clearedIntersectedEls.push(prevIntersectedEls[i]);
238237
}
239238
if (clearedIntersectedEls.length) {
240239
el.emit('raycaster-intersection-cleared', this.intersectionClearedDetail);
241240
}
242241

242+
// Emit intersected on intersected entity per intersected entity.
243+
for (i = 0; i < newIntersectedEls.length; i++) {
244+
newIntersectedEls[i].emit('raycaster-intersected', {
245+
el: el,
246+
intersection: newIntersections[i]
247+
});
248+
}
249+
250+
// Emit all intersections at once on raycasting entity.
251+
if (newIntersections.length) {
252+
el.emit('raycaster-intersection', {
253+
els: newIntersectedEls,
254+
intersections: newIntersections
255+
});
256+
}
257+
243258
// Update line length.
244-
if (data.showLine) {
245-
if (intersections.length) {
246-
if (intersections[0].object.el === el && intersections[1]) {
247-
lineLength = intersections[1].distance;
248-
} else {
249-
lineLength = intersections[0].distance;
259+
setTimeout(function () {
260+
if (self.data.showLine) {
261+
if (intersections.length) {
262+
if (intersections[0].object.el === el && intersections[1]) {
263+
lineLength = intersections[1].distance;
264+
} else {
265+
lineLength = intersections[0].distance;
266+
}
250267
}
268+
self.drawLine(lineLength);
251269
}
252-
this.drawLine(lineLength);
253-
}
270+
});
254271
};
255272
})(),
256273

‎tests/components/raycaster.test.js‎

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ suite('raycaster', function () {
184184
el.setAttribute('position', '0 0 1');
185185
el.setAttribute('raycaster', {near: 0.1, far: 10});
186186

187+
targetEl.setAttribute('id', 'target');
187188
targetEl.setAttribute('geometry', 'primitive: box');
188189
targetEl.setAttribute('position', '0 0 -1');
189190
targetEl.addEventListener('loaded', function () {
@@ -217,6 +218,35 @@ suite('raycaster', function () {
217218
component.tick();
218219
});
219220

221+
test('does not re-emit raycaster-intersection if no new intersections', function (done) {
222+
var count = 0;
223+
var raycasterEl = el;
224+
raycasterEl.addEventListener('raycaster-intersection', function () {
225+
count++;
226+
});
227+
component.tick();
228+
component.tick();
229+
setTimeout(() => {
230+
assert.equal(count, 1);
231+
done();
232+
});
233+
});
234+
235+
test('does not re-emit raycaster-intersected if previously intersecting', function (done) {
236+
var count = 0;
237+
targetEl.addEventListener('raycaster-intersected', function (evt) {
238+
count++;
239+
});
240+
component.tick();
241+
component.tick();
242+
component.tick();
243+
setTimeout(() => {
244+
// 2 because the raycaster hits the box in two points.
245+
assert.equal(count, 2);
246+
done();
247+
});
248+
});
249+
220250
test('emits event on intersected entity with details', function (done) {
221251
var raycasterEl = el;
222252
targetEl.addEventListener('raycaster-intersected', function (evt) {
@@ -244,7 +274,7 @@ suite('raycaster', function () {
244274
var raycasterEl = el;
245275
targetEl.addEventListener('raycaster-intersected', function () {
246276
// Point raycaster somewhere else.
247-
raycasterEl.setAttribute('rotation', '90 0 0');
277+
raycasterEl.setAttribute('rotation', '-90 0 0');
248278
targetEl.addEventListener('raycaster-intersected-cleared', function (evt) {
249279
assert.equal(evt.detail.el, raycasterEl);
250280
done();
@@ -433,14 +463,18 @@ suite('raycaster', function () {
433463
el.sceneEl.object3D.updateMatrixWorld();
434464
component.refreshObjects();
435465
component.tick();
436-
line = el.getAttribute('line');
437-
assert.equal(new THREE.Vector3().copy(line.start).sub(line.end).length(), 24.5);
438-
box.parentNode.removeChild(box);
439466
setTimeout(() => {
440-
component.tick();
441467
line = el.getAttribute('line');
442-
assert.equal(new THREE.Vector3().copy(line.start).sub(line.end).length(), 1000);
443-
done();
468+
assert.equal(new THREE.Vector3().copy(line.start).sub(line.end).length(), 24.5);
469+
box.parentNode.removeChild(box);
470+
setTimeout(() => {
471+
component.tick();
472+
setTimeout(() => {
473+
line = el.getAttribute('line');
474+
assert.equal(new THREE.Vector3().copy(line.start).sub(line.end).length(), 1000);
475+
done();
476+
});
477+
});
444478
});
445479
});
446480
});
@@ -458,7 +492,7 @@ suite('raycaster', function () {
458492

459493
rayEl2 = document.createElement('a-entity');
460494
rayEl2.setAttribute('raycaster', {
461-
direction: '0 -1 -1',
495+
direction: '0 0 -1',
462496
origin: '0 0 0',
463497
showLine: true,
464498
objects: '#target'
@@ -482,19 +516,23 @@ suite('raycaster', function () {
482516
component.tick();
483517
rayEl2.components.raycaster.tick();
484518
rayEl2.components.raycaster.tick();
485-
// ensure component and geometry are unaffected by other raycaster
486-
line = el.getAttribute('line');
487-
assert.equal(new THREE.Vector3().copy(line.start).sub(line.end).length(), 24.5);
488-
lineEnd.fromArray(lineArray, 3);
489-
assert.equal(lineStart.fromArray(lineArray).sub(lineEnd).length(), 24.5);
490-
box.parentNode.removeChild(box);
491519
setTimeout(() => {
492-
component.tick();
520+
// Ensure component and geometry are unaffected by other raycaster
493521
line = el.getAttribute('line');
494-
assert.equal(new THREE.Vector3().copy(line.start).sub(line.end).length(), 1000);
522+
assert.equal(new THREE.Vector3().copy(line.start).sub(line.end).length(), 24.5);
495523
lineEnd.fromArray(lineArray, 3);
496-
assert.equal(lineStart.fromArray(lineArray).sub(lineEnd).length(), 1000);
497-
done();
524+
assert.equal(lineStart.fromArray(lineArray).sub(lineEnd).length(), 24.5);
525+
box.parentNode.removeChild(box);
526+
setTimeout(() => {
527+
component.tick();
528+
setTimeout(() => {
529+
line = el.getAttribute('line');
530+
assert.equal(new THREE.Vector3().copy(line.start).sub(line.end).length(), 1000);
531+
lineEnd.fromArray(lineArray, 3);
532+
assert.equal(lineStart.fromArray(lineArray).sub(lineEnd).length(), 1000);
533+
done();
534+
});
535+
});
498536
});
499537
});
500538
});

0 commit comments

Comments
 (0)