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) {