Skip to content
Permalink
master
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
<html>
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<head>
<title>ARKit 1.5 image detection example</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link rel="stylesheet" href="../common.css"/>
<script src="../libs/three/three.min.js"></script>
<script src="../libs/three/loaders/GLTFLoader.js"></script>
<script src="../libs/dat.gui.min.js"></script>
<script src="../libs/stats.js"></script>
<script src="../libs/three-web-layer.umd.js"></script>
</head>
<body>
<div id="hubs" style="width: 100px; display: none">
<img id="hubs-image" src="hubs.png" >
</div>
<div id="description">
<h2>ARKit 1.5 Image Detection Example</h2>
<p>This sample automatically creates and activates a reference image, and places an anchored ducky when it's detected. Print hubs.png so that it's 20cm wide.</p>
</div>
<button type=button id=go-button>Go</button>
<br>
<div id="reset"></div>
<script type=module>
// some dependencies and utilities
import * as mat4 from '../libs/gl-matrix/mat4.js';
import * as vec3 from '../libs/gl-matrix/vec3.js';
// import * as THREE from '../libs/three.js';
import XREngine from '../XREngine.js';
// import * as WebLayer3D from '../libs/three-web-layer.es5.js'
let session = null;
let localReferenceSpace = null;
let viewerReferenceSpace = null;
let engine = null;
const meshMap = new Map();
// temporary working variables
const workingMatrix = mat4.create();
const workingVec3 = vec3.create();
let savedOrigin = [0,0,0];
let savedDirection = [0,0,-1];
let ambientLight = null;
let directionalLight = null;
let duckyCreated = false;
let imageDetectionCreationRequested = false;
let imageActivateDetection = false;
let imageActivated = false;
let imageAnchor = null;
let ducky = new THREE.Group();
ducky.name = 'Duck group';
const loader = new THREE.GLTFLoader().setPath('./');
loader.load('DuckyMesh.glb',
gltf => {
ducky = new THREE.Group();
ducky.name = 'Duck group';
const duckyNode = gltf.scene;
duckyNode.position.set(0, -0.01, 0);
ducky.add(duckyNode);
duckyCreated = true;
},
null, // progress callback
e => {
console.error('could not load gltf', e);
}
);
// set up some stats, including a new pane for CV fps
const stats = new Stats();
stats.domElement.style.cssText = 'position:fixed;top:2%;right:2%;cursor:pointer;opacity:0.9;z-index:10000';
document.body.appendChild(stats.dom);
const goButton = document.getElementById('go-button');
const initXR = () => {
if (navigator.xr) {
navigator.xr.isSessionSupported('immersive-ar', ['worldSensing']).then(supported => {
if (supported) {
goButton.disabled = false;
goButton.addEventListener('click', onButtonClick);
} else {
goButton.initText = 'No WebXR AR support';
}
});
} else {
goButton.initText = 'No WebXR support';
}
};
const onButtonClick = event => {
if (!session) {
navigator.xr.requestSession('immersive-ar', {requiredFeatures: ['worldSensing']})
.then(xrSession => {
initSession(xrSession);
goButton.innerText = 'End';
}).catch(err => {
console.error('Session setup error', err);
});
} else {
session.end();
}
};
const initSession = async xrSession => {
session = xrSession;
session.addEventListener('end', onSessionEnd);
localReferenceSpace = await session.requestReferenceSpace('local');
viewerReferenceSpace = await session.requestReferenceSpace('viewer');
// Create the context where we will render our 3D scene
const canvas = document.createElement('canvas');
const context = canvas.getContext('webgl', {
xrCompatible: true
});
if (!context) throw new Error('Could not create a webgl context');
session.nonStandard_setNumberOfTrackedImages(4);
// Set up the base layer
session.updateRenderState({baseLayer: new XRWebGLLayer(session, context)});
// Create a simple test scene and renderer
// The engine's scene is in the eye-level coordinate system
engine = new XREngine(canvas, context);
// get the location of the device, and use it to create an
// anchor with the identity orientation
session.requestAnimationFrame(async (t, frame) => {
mat4.copy(workingMatrix, frame.getPose(localReferenceSpace, viewerReferenceSpace).transform.matrix);
mat4.getTranslation(workingVec3, workingMatrix);
mat4.fromTranslation(workingMatrix, workingVec3);
const anchor = await frame.addAnchor(workingMatrix, localReferenceSpace);
engine.addAnchoredNode(anchor, engine.root);
// Kick off rendering
session.requestAnimationFrame(handleAnimationFrame);
});
// initialize scene
ambientLight = engine.addAmbientLight();
directionalLight = engine.addDirectionalLight();
};
const onSessionEnd = event => {
session = null;
viewerReferenceSpace = null;
localReferenceSpace = null;
imageDetectionCreationRequested = false;
imageActivateDetection = false;
imageActivated = false;
imageAnchor = null;
document.getElementById('go-button').innerText = 'Go';
goButton.innerText = 'Go';
};
// Called once per frame, before render, to give the app a chance to update this.scene
const updateScene = frame => {
stats.update();
const hubsImageName = 'hubs';
frame.getGlobalLightEstimate().then(lightProbe => {
const ambientIntensity = lightProbe.indirectIrradiance; // @TODO: Fix me
ambientLight.intensity = ambientIntensity;
directionalLight.intensity = ambientIntensity * 0.5;
});
if (!imageDetectionCreationRequested && duckyCreated) {
imageDetectionCreationRequested = true;
let hubsImageData = getImageData('hubs-image');
session.nonStandard_createDetectionImage(hubsImageName, hubsImageData.data, hubsImageData.width, hubsImageData.height, 0.2).then(() => {
// TODO: Can't do any of this until we can put a DOM Overlay in place.
//
// let resetButton = document.createElement('button');
// resetButton.setAttribute('class', 'reset-button');
// resetButton.innerText = 'Reset';
// document.getElementById('reset').appendChild(resetButton);
// resetButton.addEventListener('click', ev => {
// imageActivateDetection = true;
// //engine.removeAnchoredNode(ducky)
// if (imageAnchor) {
// imageAnchor.detach();
// engine.removeAnchoredNode(ducky);
// imageAnchor = null;
// }
// });
// ready to go!
imageActivateDetection = true;
}).catch(error => {
console.error(`error creating ducky detection image: ${error}`)
});
}
if (!imageActivated && imageActivateDetection) {
imageActivated = true;
imageActivateDetection = false;
session.nonStandard_activateDetectionImage(hubsImageName).then(anchor => {
imageActivated = false;
imageAnchor = anchor;
imageAnchor.addEventListener('remove', event => {
imageActivated = false;
});
engine.addAnchoredNode(imageAnchor, ducky);
}).catch(error => {
imageActivated = false;
console.error(`error activating ducky detection image: ${error}`);
});
}
};
const getImageData = imageID => {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const img = document.getElementById(imageID);
canvas.width = img.width;
canvas.height = img.height;
context.drawImage(img, 0, 0);
return context.getImageData(0, 0, img.width, img.height);
};
// render loop
const handleAnimationFrame = (t, frame) => {
if (!session || session.ended) return
session.requestAnimationFrame(handleAnimationFrame);
updateScene(frame);
const pose = frame.getViewerPose(localReferenceSpace);
if (!pose) {
console.log('No pose');
return;
}
engine.startFrame();
for (const view of pose.views) {
engine.preRender(
session.renderState.baseLayer.getViewport(view),
view.projectionMatrix,
view.transform.matrix
);
engine.render();
}
engine.endFrame();
}
initXR();
</script>
</body>
</html>