diff --git a/examples/index.html b/examples/index.html index 693491e6f48..5dd77636e9e 100644 --- a/examples/index.html +++ b/examples/index.html @@ -233,6 +233,7 @@

Tests

  • Physically-Based Materials
  • Pivot
  • Raycaster
  • +
  • Raycaster (Simple)
  • Shaders
  • Text
  • Text Anchors
  • diff --git a/examples/test/raycaster/simple.html b/examples/test/raycaster/simple.html new file mode 100644 index 00000000000..c2c51f80acc --- /dev/null +++ b/examples/test/raycaster/simple.html @@ -0,0 +1,34 @@ + + + + + Raycaster + + + + + + + + + + + diff --git a/src/components/raycaster.js b/src/components/raycaster.js index dc935dd8f3b..83108ffd5d3 100644 --- a/src/components/raycaster.js +++ b/src/components/raycaster.js @@ -33,15 +33,18 @@ module.exports.Component = registerComponent('raycaster', { this.raycaster = new THREE.Raycaster(); this.updateOriginDirection(); this.refreshObjects = bind(this.refreshObjects, this); + this.refreshOnceChildLoaded = bind(this.refreshOnceChildLoaded, this); }, play: function () { - this.el.sceneEl.addEventListener('child-attached', this.refreshObjects); + this.el.sceneEl.addEventListener('loaded', this.refreshObjects); + this.el.sceneEl.addEventListener('child-attached', this.refreshOnceChildLoaded); this.el.sceneEl.addEventListener('child-detached', this.refreshObjects); }, pause: function () { - this.el.sceneEl.removeEventListener('child-attached', this.refreshObjects); + this.el.sceneEl.removeEventListener('loaded', this.refreshObjects); + this.el.sceneEl.removeEventListener('child-attached', this.refreshOnceChildLoaded); this.el.sceneEl.removeEventListener('child-detached', this.refreshObjects); }, @@ -59,6 +62,21 @@ module.exports.Component = registerComponent('raycaster', { this.refreshObjects(); }, + /** + * Update list of objects to test for intersection once child is loaded. + */ + refreshOnceChildLoaded: function (evt) { + var self = this; + var el = evt.detail.el; + if (!el) { return; } + if (el.hasLoaded) { this.refreshObjects(); } else { + el.addEventListener('loaded', function nowRefresh (evt) { + el.removeEventListener('loaded', nowRefresh); + self.refreshObjects(); + }); + } + }, + /** * Update list of objects to test for intersection. */ @@ -89,35 +107,8 @@ module.exports.Component = registerComponent('raycaster', { // Add the object3D's children so non-recursive raycasting will work correctly. for (j = 0; j < children.length; j++) { this.objects.push(children[j]); } } else { - // If there aren't any children, then at least add the object3D itself; - // in that case, only recursive raycasting will function properly. - // Unhappily, this is the default case :-/ - object.needsRefresh = true; - this.objects.push(object); - } - } - }, - - /** - * Update Groups in list of objects to test for intersection. - */ - refreshGroupObjects: function () { - var object3D; - if (!this.objects) { return; } - for (var i = 0; i < this.objects.length; i++) { - object3D = this.objects[i]; - if (!object3D.needsRefresh) { continue; } - - // If the object is linked to an element and its object3D changed, use that - if (object3D.el && object3D.el.object3D !== object3D) { - object3D = object3D.el.object3D; - } - - // If the object is a Group, and has one child, use that instead. - if (object3D.type === 'Group' && object3D.children.length === 1) { - this.objects[i] = object3D = object3D.children[0]; - // Only do this check once (if it works). - object3D.needsRefresh = false; + // If there aren't any children, then until a refresh after geometry loads, + // raycast won't see this object... but that should happen automatically. } } }, @@ -144,7 +135,6 @@ module.exports.Component = registerComponent('raycaster', { // Raycast. this.updateOriginDirection(); - this.refreshGroupObjects(); intersections = this.raycaster.intersectObjects(this.objects, data.recursive); // Only keep intersections against objects that have a reference to an entity. diff --git a/tests/components/raycaster.test.js b/tests/components/raycaster.test.js index c69a5be51c7..2048b99812f 100644 --- a/tests/components/raycaster.test.js +++ b/tests/components/raycaster.test.js @@ -104,6 +104,8 @@ suite('raycaster', function () { var newEl = document.createElement('a-entity'); var numObjects = el.components.raycaster.objects.length; var sceneEl = this.el.sceneEl; + // add some geometry so raycast will actually work + newEl.setAttribute('geometry', 'primitive: box'); sceneEl.addEventListener('child-attached', eventuallyDoAssert); sceneEl.appendChild(newEl); @@ -111,11 +113,9 @@ suite('raycaster', function () { if (newEl.hasLoaded) { doAssert(); } else { newEl.addEventListener('loaded', doAssert); } } function doAssert () { - // FIXME: getting 5 not equaling 2 - // I think the extra 3 are default injection of camera and two lights - assert.equal(el.components.raycaster.objects.length, numObjects + 1); sceneEl.removeEventListener('child-attached', eventuallyDoAssert); newEl.removeEventListener('loaded', doAssert); + assert.equal(el.components.raycaster.objects.length, numObjects + 1); done(); } }); @@ -125,6 +125,8 @@ suite('raycaster', function () { var newEl = document.createElement('a-entity'); var numObjects = el.components.raycaster.objects.length; var sceneEl = this.el.sceneEl; + // add some geometry so raycast will actually work + newEl.setAttribute('geometry', 'primitive: box'); sceneEl.addEventListener('child-detached', doAssert); sceneEl.addEventListener('child-attached', eventuallyDoRemove); sceneEl.appendChild(newEl); @@ -134,8 +136,6 @@ suite('raycaster', function () { } function doRemove () { sceneEl.removeChild(newEl); } function doAssert () { - // FIXME: getting 4 not equaling 1 - // I think the extra 3 are default injection of camera and two lights assert.equal(el.components.raycaster.objects.length, numObjects); sceneEl.removeEventListener('child-attached', eventuallyDoRemove); sceneEl.removeEventListener('child-detached', doAssert); @@ -156,18 +156,16 @@ suite('raycaster', function () { }); targetEl.setAttribute('geometry', 'primitive: box; depth: 1; height: 1; width: 1;'); - targetEl.setAttribute('material', ''); targetEl.setAttribute('position', '0 0 -1'); - targetEl.addEventListener('loaded', function finishSetup () { - done(); - }); el.sceneEl.appendChild(targetEl); + // `npm run test:forefox` needs the timeout for the tests to succeed. + function finishSetup () { setTimeout(function () { done(); }, 0); } + if (targetEl.hasLoaded) { finishSetup(); } else { targetEl.addEventListener('loaded', finishSetup); } }); test('can catch basic intersection', function (done) { this.targetEl.addEventListener('raycaster-intersected', function () { done(); }); this.el.sceneEl.tick(); - this.el.sceneEl.tick(); }); test('updates intersectedEls', function (done) { @@ -246,14 +244,17 @@ suite('raycaster', function () { }); targetEl.setAttribute('geometry', 'primitive: box; depth: 1; height: 1; width: 1;'); - targetEl.setAttribute('material', ''); targetEl.setAttribute('position', '0 0 -1'); - targetEl.addEventListener('loaded', function finishSetup () { - // The object to check raycast against should reference the entity. - assert.equal(targetEl, targetEl.object3D.children[0].el); - done(); - }); el.sceneEl.appendChild(targetEl); + // `npm run test:forefox` needs the timeout for the tests to succeed. + function finishSetup () { + setTimeout(function () { + // The object to check raycast against should reference the entity. + assert.equal(targetEl, targetEl.object3D.children[0].el); + done(); + }, 0); + } + if (targetEl.hasLoaded) { finishSetup(); } else { targetEl.addEventListener('loaded', finishSetup); } }); test('can catch basic intersection', function (done) {