diff --git a/examples/spawn-at-surface-with-anchors.html b/examples/spawn-at-surface-with-anchors.html index 603c09a..a35f1af 100644 --- a/examples/spawn-at-surface-with-anchors.html +++ b/examples/spawn-at-surface-with-anchors.html @@ -179,6 +179,10 @@ function init() { // Initialize the anchor manager anchorManager = new THREE.ARAnchorManager(vrDisplay); + // Just show a message when anchors update. + anchorManager.addEventListener("anchorsupdated", function(event) { + console.log(event.object3ds.length + " Object3D-s updated their pose!"); + }); // Bind our event handlers window.addEventListener('resize', onWindowResize, false); @@ -235,7 +239,7 @@ function onClick (e) { // Remove cubes with more than one finger. if (e.touches.length > 1 && cubes.length > 0) { scene.remove(cubes[0]); - anchorManager.removeAnchorForObject3D(cubes[0]); + anchorManager.remove(cubes[0]); cubes.splice(0, 1); return; } @@ -258,15 +262,15 @@ function onClick (e) { // Use the `placeObjectAtHit` utility to position // the cube where the hit occurred THREE.ARUtils.placeObjectAtHit(cubeClone, // The object to place - hit, // The VRHit object to move the cube to - 1, // Easing value from 0 to 1; we want to move - // the cube directly to the hit position - true); // Whether or not we also apply orientation + hit, // The VRHit object to move the cube to + 1, // Easing value from 0 to 1; we want to move + // the cube directly to the hit position + true); // Whether or not we also apply orientation + anchorManager.add(cubeClone); + scene.add(cubeClone); + cubes.push(cubeClone); } - anchorManager.addAnchorForObject3D(cubeClone); - scene.add(cubeClone); - cubes.push(cubeClone); } diff --git a/package-lock.json b/package-lock.json index 6528d54..19c005b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -155,7 +155,7 @@ "babel-eslint": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-8.0.1.tgz", - "integrity": "sha1-XXGL56MoYl0AYCLrKT7TAIy9Y0Y=", + "integrity": "sha512-h3moF6PCTQE06UjMMG+ydZSBvZ4Q7rqPE/5WAUOvUyHYUTqxm8JVhjZRiG1avI/tGVOK4BnZLDQapyLzh8DeKg==", "dev": true, "requires": { "babel-code-frame": "7.0.0-beta.0", @@ -167,7 +167,7 @@ "ansi-styles": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha1-wVm41b4PnlpvNG2rlPFs4CIWG4g=", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { "color-convert": "1.9.0" @@ -176,7 +176,7 @@ "babel-code-frame": { "version": "7.0.0-beta.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-7.0.0-beta.0.tgz", - "integrity": "sha1-QYp7Xz99yaRnDmGxFYtMVmG+yY0=", + "integrity": "sha512-/xr1ADm5bnTjjN+xwoXb7lF4v2rnxMzNZzFU7h8SxB+qB6+IqSTOOqVcpaPTUC2Non/MbQxS3OIZnJpQ2X21aQ==", "dev": true, "requires": { "chalk": "2.2.0", @@ -187,7 +187,7 @@ "babel-helper-function-name": { "version": "7.0.0-beta.0", "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-7.0.0-beta.0.tgz", - "integrity": "sha1-0bZ3m2R+XFwx6+sF4TuZjk01LVY=", + "integrity": "sha512-DaQccFBBWBEzMdqbKmNXamY0m1yLHJGOdbbEsNoGdJrrU7wAF3wwowtDDPzF0ZT3SqJXPgZW/P2kgBX9moMuAA==", "dev": true, "requires": { "babel-helper-get-function-arity": "7.0.0-beta.0", @@ -199,7 +199,7 @@ "babel-helper-get-function-arity": { "version": "7.0.0-beta.0", "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-7.0.0-beta.0.tgz", - "integrity": "sha1-nRq3ITu17+HvFjio6hSJlptai24=", + "integrity": "sha512-csqAic15/2Vm1951nJxkkL9K8E6ojyNF/eAOjk7pqJlO8kvgrccGNFCV9eDwcGHDPe5AjvJGwVSAcQ5fit9wuA==", "dev": true, "requires": { "babel-types": "7.0.0-beta.0" @@ -208,13 +208,13 @@ "babel-messages": { "version": "7.0.0-beta.0", "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-7.0.0-beta.0.tgz", - "integrity": "sha1-bfASluSfyPvQY3OUMmoWfzbagXs=", + "integrity": "sha512-eXdShsm9ZTh9AQhlIaAn6HR3xWpxCnK9ZwIDA9QyjnwTgMctGxHHflw4b4RJ3/ZjTL0Vrmvm0tQXPkp49mTAUw==", "dev": true }, "babel-template": { "version": "7.0.0-beta.0", "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-7.0.0-beta.0.tgz", - "integrity": "sha1-hQg8+eQ5XV5Iv1FU16jWmRyv7Ps=", + "integrity": "sha512-tmdH+MmmU0F6Ur8humpevSmFzYKbrN3Oru0g5Qyg4R6+sxjnzZmnvzUbsP0aKMr7tB0Ua6xhEb9arKTOsEMkyA==", "dev": true, "requires": { "babel-traverse": "7.0.0-beta.0", @@ -226,7 +226,7 @@ "babel-traverse": { "version": "7.0.0-beta.0", "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-7.0.0-beta.0.tgz", - "integrity": "sha1-2hS+m3YvYqLwYNtGTqr92M0HKkE=", + "integrity": "sha512-IKzuTqUcQtMRZ0Vv5RjIrGGj33eBKmNTNeRexWSyjPPuAciyNkva1rt7WXPfHfkb+dX7coRAIUhzeTUEzhnwdA==", "dev": true, "requires": { "babel-code-frame": "7.0.0-beta.0", @@ -243,7 +243,7 @@ "babel-types": { "version": "7.0.0-beta.0", "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-7.0.0-beta.0.tgz", - "integrity": "sha1-64tuVWRw5tzErvmC15rSKUabUWk=", + "integrity": "sha512-rJc2kV9iPJGLlqIY71AM3nPcdkoeLRCDuR07GFgfd3lFl4TsBQq76TxYQQIZ2MONg1HpsqmuoCXr9aZ1Oa4wYw==", "dev": true, "requires": { "esutils": "2.0.2", @@ -254,7 +254,7 @@ "babylon": { "version": "7.0.0-beta.22", "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.22.tgz", - "integrity": "sha1-dPCtgu18fDz+q3TPaE+BUQQWG2U=", + "integrity": "sha512-Yl7iT8QGrS8OfR7p6R12AJexQm+brKwrryai4VWZ7NHUbPoZ5al3+klhvl/14shXZiLa7uK//OIFuZ1/RKHgoA==", "dev": true }, "chalk": { @@ -271,7 +271,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" diff --git a/src/ARAnchorManager.js b/src/ARAnchorManager.js index 261618a..383d018 100644 --- a/src/ARAnchorManager.js +++ b/src/ARAnchorManager.js @@ -1,14 +1,13 @@ -// import { EventEmitter } from 'events'; -import { Object3D, Vector3, Matrix4 } from 'three'; +import { Object3D, Vector3, Matrix4, EventDispatcher } from 'three'; /** * A class to manage registration and unregistration of anchors * for any THREE.Object3D. Anchors are world space and handle updating * the position of the object to which one is assigned. * AnchorManager emits events pertaining to anchors. */ -export default class ARAnchorManager /* extends EventEmitter */ { +export default class ARAnchorManager extends EventDispatcher { constructor(vrDisplay) { - // super(); + super(); if (!(vrDisplay instanceof VRDisplay) || !vrDisplay.getAnchors) { throw new Error("invalid vr display parameter"); } @@ -24,38 +23,58 @@ export default class ARAnchorManager /* extends EventEmitter */ { // anchor creation code only using the position and quaternion from the // object3d. this.matrix_ = new Matrix4(); - return this; } /** * Assign a new anchor to an THREE.Object3D. + * @param object3d {THREE.Object3D} A Three Object3D instance to create and + * manage an anchor for. The current transformation (position/orientation) of + * the Object3D will be used to create the anchor and this class will keep + * it up to date with any tracking changes detected by the system. + * IMPORTANT: The passed Object3D instance should be in world space! + * @throws Error if: + * - The given object3d is not an instance of Object3D. + * */ - addAnchorForObject3D(object3d) { + add(object3d) { if (!(object3d instanceof Object3D)) { - throw new Error('invalid object3d trying to add an anchor'); + throw new Error('Invalid Object3D trying to add an anchor'); }; // Do not allow to create an anchor for an object3d that already has one. - if (this.object3DsToAnchors_.get(object3d)) { - return; + if (this.object3DsToAnchors_.has(object3d)) { + return this; } + // If the object has a parent and its parent has a parent, it is very + // likely that it may not be in world space so warn the developer. + // NOTE: Commented out for now because if we use anchors for planes in + // ARPlanes, they are added to a identity main parent object but does not + // mean they are not in world space. Getting this warning at all times is + // not optimal. +// if (object3d.parent && object3d.parent.parent) { +// console.warn("anchors should be added for Object3D-s in world space"); +// } // Calculate the model matrix of the object3d using the position and // quaternion of the object. this.scale_.set(1, 1, 1); this.matrix_.compose(object3d.position, object3d.quaternion, this.scale_); - // use the world matrix incase the object is a child of a parent. - // "matrix" is relative to parent, "worldMatrix" is world space. const anchor = this.vrDisplay_.addAnchor(this.matrix_.elements); // Store the anchor and the object3d in the structure to retrieve them // later on. this.anchorsToObject3Ds_.set(anchor, object3d); this.object3DsToAnchors_.set(object3d, anchor); + + console.log("anchor " + anchor.identifier + " added for object3d " + object3d.id); + return this; } + /** * Delete anchor from the THREE.Object3D. + * @return {boolean} a flag that indicates if the anchor was correctly removed for + * the provided object3d. */ - removeAnchorForObject3D(object3d) { + remove(object3d) { if (!(object3d instanceof Object3D)) { - throw new Error('invalid object3d trying to remove anchor'); + throw new Error('Invalid Object3D trying to remove anchor'); }; const anchor = this.object3DsToAnchors_.get(object3d); if (!anchor) { @@ -64,6 +83,9 @@ export default class ARAnchorManager /* extends EventEmitter */ { this.anchorsToObject3Ds_.delete(anchor); this.object3DsToAnchors_.delete(object3d); this.vrDisplay_.removeAnchor(anchor); + + console.log("anchor " + anchor.identifier + " removed for object3d " + object3d.id); + return true; } /** @@ -73,12 +95,12 @@ export default class ARAnchorManager /* extends EventEmitter */ { */ onAnchorsUpdated_(event) { const updatedObject3Ds = []; - for (var i = 0; i < event.anchors.length; i++) { - var anchor = event.anchors[i]; + for (let anchor of event.anchors) { const object3d = this.anchorsToObject3Ds_.get(anchor); if (!object3d) { throw new Error("this should never happen: " + - "an anchor without the corresponding object3d."); + "an anchor (identifier = " + anchor.identifier + + ") without the corresponding object3d."); }; // get the anchor's updated model matrix object3d.matrix.fromArray(anchor.modelMatrix); @@ -88,6 +110,8 @@ export default class ARAnchorManager /* extends EventEmitter */ { this.scale_); updatedObject3Ds.push(object3d); } - // this.emit('object3dsupdated', updatedObject3Ds); + this.dispatchEvent({ + type: 'anchorsupdated', + object3ds: updatedObject3Ds}); } } \ No newline at end of file diff --git a/src/ARPlanes.js b/src/ARPlanes.js index a4add16..623523d 100644 --- a/src/ARPlanes.js +++ b/src/ARPlanes.js @@ -27,6 +27,7 @@ import { import { getRandomPaletteColor } from './ARUtils'; import vertexShader from './shaders/arplanes.vert'; import fragmentShader from './shaders/arplanes.frag'; +import ARAnchorManager from './ARAnchorManager'; const DEFAULT_MATERIAL = new RawShaderMaterial({ side: DoubleSide, @@ -73,6 +74,8 @@ class ARPlanes extends Object3D { * @type {Map { let existing = this.planes.get(identifier); if (existing) { + this.anchorManager.remove(existing); this.remove(existing); } this.planes.delete(identifier);