Snippets

Sam Gates Three.js Hand Tracking?

Created by Sam Gates
<!DOCTYPE html>
<html lang="en">
	<head>
		<title>three.js ar - hit test</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
		<link type="text/css" rel="stylesheet" href="main.css">
	</head>
	
	<p id="coord" style="color:white; z-index: 10000;">N/A</p>
	<body>

		<div id="info">
			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> ar - hit test<br/>(Chrome Android 81+)
			
		</div>
		<script type="module">

			import * as THREE from '../build/three.module.js';
			import { ARButton } from './jsm/webxr/ARButton.js';

			let container;
			let camera, scene, renderer;
			let controller;

			let reticle;

			let hitTestSource = null;
			let hitTestSourceRequested = false;
			var cube;

			var coord = document.getElementById("coord");

			init();
			animate();

			function init() {

				container = document.createElement( 'div' );
				document.body.appendChild( container );

				scene = new THREE.Scene();

				camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 20 );

				const light = new THREE.HemisphereLight( 0xffffff, 0xbbbbff, 1 );
				light.position.set( 0.5, 1, 0.25 );
				scene.add( light );

				//

				renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
				renderer.setPixelRatio( window.devicePixelRatio );
				renderer.setSize( window.innerWidth/2, window.innerHeight/2 );
				renderer.xr.enabled = true;
				container.appendChild( renderer.domElement );

				//

				document.body.appendChild( ARButton.createButton( renderer, { requiredFeatures: [ 'hit-test' ] } ) );

				//

				const geometry = new THREE.CylinderBufferGeometry( 0.1, 0.1, 0.2, 32 ).translate( 0, 0.1, 0 );

				function onSelect() {

					if ( reticle.visible ) {

						const material = new THREE.MeshPhongMaterial( { color: 0xffffff * Math.random() } );
						const mesh = new THREE.Mesh( geometry, material );
						mesh.position.setFromMatrixPosition( reticle.matrix );
						mesh.scale.y = Math.random() * 2 + 1;
						scene.add( mesh );

					}

				}

				controller = renderer.xr.getController( 0 );
				controller.addEventListener( 'select', onSelect );
				scene.add( controller );

				reticle = new THREE.Mesh(
					new THREE.RingBufferGeometry( 0.15, 0.2, 32 ).rotateX( - Math.PI / 2 ),
					new THREE.MeshBasicMaterial()
				);
				reticle.matrixAutoUpdate = false;
				reticle.visible = false;
				scene.add( reticle );

				var cgeom = new THREE.BoxGeometry(.25,.25,.25);
				var cmat = new THREE.MeshPhongMaterial({color: 0xff0000});
				cube = new THREE.Mesh(cgeom, cmat);
				
				scene.add(cube);

				//

				window.addEventListener( 'resize', onWindowResize, false );

			}

			function onWindowResize() {

				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();

				renderer.setSize( window.innerWidth, window.innerHeight );

			}

			//

			function animate() {

				renderer.setAnimationLoop( render );

			}

			function render( timestamp, frame ) {

				if ( frame ) {

					const referenceSpace = renderer.xr.getReferenceSpace();
					const session = renderer.xr.getSession();

					var pos = renderer.xr.getHand(0).position;
					cube.position.set(pos.x, pos.y, pos.z);
					coord.innerHTML="("+pos.x+", "+pos.y+", "+pos.z+")";
					
					if ( hitTestSourceRequested === false ) {

						session.requestReferenceSpace( 'viewer' ).then( function ( referenceSpace ) {

							session.requestHitTestSource( { space: referenceSpace } ).then( function ( source ) {

								hitTestSource = source;

							} );

						} );

						session.addEventListener( 'end', function () {

							hitTestSourceRequested = false;
							hitTestSource = null;

						} );

						hitTestSourceRequested = true;

					}

					if ( hitTestSource ) {

						const hitTestResults = frame.getHitTestResults( hitTestSource );

						if ( hitTestResults.length ) {

							const hit = hitTestResults[ 0 ];

							reticle.visible = true;
							reticle.matrix.fromArray( hit.getPose( referenceSpace ).transform.matrix );

						} else {

							reticle.visible = false;

						}

					}

				}

				renderer.render( scene, camera );

			}

		</script>
	</body>
</html>

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.