@google/model-viewer
Version:
Easily display interactive 3D models on the web and in AR!
95 lines (77 loc) • 2.95 kB
text/typescript
/*
* Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Camera, Math as ThreeMath, Matrix4, Mesh, MeshBasicMaterial, Object3D, Raycaster, RingGeometry, Vector3,} from 'three';
const originArray = new Float32Array(3);
const directionArray = new Float32Array(3);
/**
* The Reticle class creates an object that repeatedly calls
* `xrSession.requestHitTest()` to render a ring along a found
* horizontal surface.
*/
export default class Reticle extends Object3D {
private ring: Mesh;
private camera: Camera;
private raycaster: Raycaster|null = null;
/**
* @param {XRSession} xrSession
* @param {THREE.Camera} camera
*/
constructor(camera: Camera) {
super();
this.name = 'Reticle';
let geometry = new RingGeometry(0.1, 0.11, 24, 1);
let material = new MeshBasicMaterial({color: 0xffffff});
// Orient the geometry so its position is flat on a horizontal surface
geometry.applyMatrix(new Matrix4().makeRotationX(ThreeMath.degToRad(-90)));
this.ring = new Mesh(geometry, material);
this.add(this.ring);
this.visible = false;
this.camera = camera;
}
/**
* Fires a hit test in the middle of the screen and places the reticle
* upon the surface if found.
*
* @param {XRSession} session
* @param {XRFrameOfReference} frameOfRef
*/
async update(session: XRSession, frameOfRef: XRFrameOfReference) {
this.raycaster = this.raycaster || new Raycaster();
this.raycaster.setFromCamera({x: 0, y: 0}, this.camera);
const ray = this.raycaster.ray;
originArray.set(ray.origin.toArray());
directionArray.set(ray.direction.toArray());
let hits: Array<XRHitResult>;
try {
hits =
await session.requestHitTest(originArray, directionArray, frameOfRef);
} catch (error) {
hits = [];
}
if (hits.length) {
const hit = hits[0];
const hitMatrix = new Matrix4().fromArray(hit.hitMatrix as any);
// Now apply the position from the hitMatrix onto our model
this.position.setFromMatrixPosition(hitMatrix);
// Rotate the anchor to face the camera
const targetPos =
new Vector3().setFromMatrixPosition(this.camera.matrixWorld);
const angle = Math.atan2(
targetPos.x - this.position.x, targetPos.z - this.position.z);
this.rotation.set(0, angle, 0);
this.visible = true;
}
}
}