68 lines
2.2 KiB
TypeScript
68 lines
2.2 KiB
TypeScript
import * as THREE from 'three';
|
|
import type { SceneHelper } from './sceneHelper';
|
|
import './bvh';
|
|
|
|
export type HitTest = {
|
|
objects: THREE.Intersection<THREE.Object3D>[];
|
|
}
|
|
|
|
export type HitTestRaycasterOptions = {
|
|
cameraPixelSize: THREE.Vector2Like;
|
|
tolerancePixels: number;
|
|
}
|
|
|
|
export type HitTestOptions = HitTestRaycasterOptions & {
|
|
}
|
|
|
|
export class HitTestFactory {
|
|
|
|
private static raycasters: [THREE.Vector2, THREE.Raycaster][] = Array(9).fill(0).map(() => [new THREE.Vector2(), new THREE.Raycaster()]);
|
|
|
|
private static setupRaycasters(cursor: THREE.Vector2, camera: THREE.PerspectiveCamera, options: HitTestRaycasterOptions) {
|
|
|
|
this.raycasters[0][0].copy(cursor);
|
|
this.raycasters[0][1].setFromCamera(cursor, camera);
|
|
|
|
const count = HitTestFactory.raycasters.length - 1;
|
|
const step = Math.PI * 2 / count;
|
|
|
|
for (let angle = 0, idx = 0; idx < count; angle += step, idx++) {
|
|
const pos = {
|
|
x: Math.cos(angle) * options.tolerancePixels * options.cameraPixelSize.x,
|
|
y: Math.sin(angle) * options.tolerancePixels * options.cameraPixelSize.y,
|
|
};
|
|
const v = HitTestFactory.raycasters[idx + 1][0];
|
|
v.copy(cursor).add(pos);
|
|
HitTestFactory.raycasters[idx + 1][1].setFromCamera(v, camera);
|
|
}
|
|
}
|
|
|
|
public static getRaycasterPosition(index: number): THREE.Vector2 {
|
|
return HitTestFactory.raycasters[index][0];
|
|
}
|
|
|
|
public static get raycasterCount(): number {
|
|
return HitTestFactory.raycasters.length;
|
|
}
|
|
|
|
public static hitTest(scene: SceneHelper, cursor: THREE.Vector2, camera: THREE.PerspectiveCamera, options: HitTestOptions): HitTest {
|
|
|
|
HitTestFactory.setupRaycasters(cursor, camera, options);
|
|
|
|
const objects: THREE.Object3D[] = scene.objects;
|
|
|
|
const hitTest: Record<string, THREE.Intersection<THREE.Object3D>> = {};
|
|
|
|
HitTestFactory.raycasters.forEach((raycaster) => {
|
|
const hits = raycaster[1].intersectObjects(objects);
|
|
for (const hit of hits) {
|
|
hitTest[hit.object.uuid] = hit;
|
|
}
|
|
});
|
|
|
|
return {
|
|
objects: Object.values(hitTest),
|
|
}
|
|
}
|
|
}
|