removed multiray hittest helper
This commit is contained in:
parent
b583eaee29
commit
cf45752848
|
|
@ -7,10 +7,10 @@ export const HitTestView = observer(function () {
|
||||||
<div id="hit-test">
|
<div id="hit-test">
|
||||||
<pre>
|
<pre>
|
||||||
{
|
{
|
||||||
state.hitTest.objects.map((obj) =>
|
state.hitTest.hits.map((hit) =>
|
||||||
<div key={obj.object.uuid}>
|
<div key={hit.object.uuid}>
|
||||||
{JSON.stringify(obj.point.toArray())}
|
{JSON.stringify(hit.point.toArray())}
|
||||||
{JSON.stringify(obj.object.userData)}
|
{JSON.stringify(hit.object.userData)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ import { useEffect, useRef } from "react";
|
||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import { useInteraction, type InteractionMouseEventArgs } from "../helpers/hooks/useInteration";
|
import { useInteraction, type InteractionMouseEventArgs } from "../helpers/hooks/useInteration";
|
||||||
import { db } from "../backend/db";
|
import { db } from "../backend/db";
|
||||||
import { HitTestFactory, type HitTest } from "../helpers/hitTest";
|
|
||||||
import { model } from "../model/model";
|
import { model } from "../model/model";
|
||||||
import { SceneHelper } from "../helpers/sceneHelper";
|
import { SceneHelper } from "../helpers/sceneHelper";
|
||||||
|
import type { HitResults } from "../helpers/circularFrustumIntersect";
|
||||||
|
|
||||||
export type ThreeViewEventArgs = {
|
export type ThreeViewEventArgs = {
|
||||||
camera: THREE.Camera,
|
camera: THREE.Camera,
|
||||||
|
|
@ -17,7 +17,7 @@ export type ThreeViewTickEventArgs = ThreeViewEventArgs & {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ThreeViewMouseEventArgs = ThreeViewEventArgs & {
|
export type ThreeViewMouseEventArgs = ThreeViewEventArgs & {
|
||||||
hitTest: HitTest,
|
hitResults: HitResults,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ThreeViewProps = {
|
export type ThreeViewProps = {
|
||||||
|
|
@ -102,38 +102,27 @@ export const ThreeView = function (props: ThreeViewProps) {
|
||||||
window.addEventListener("resize", handleWindowResize);
|
window.addEventListener("resize", handleWindowResize);
|
||||||
|
|
||||||
handleHover = (e: InteractionMouseEventArgs) => {
|
handleHover = (e: InteractionMouseEventArgs) => {
|
||||||
|
const hitResults = props.sceneHelper.hitTest(
|
||||||
const ht = props.sceneHelper.hitTest(
|
|
||||||
e.position,
|
e.position,
|
||||||
e.screenSize,
|
e.screenSize,
|
||||||
);
|
);
|
||||||
console.log(JSON.stringify(ht.map((h) => h.object.userData)));
|
|
||||||
|
|
||||||
const hitTest = HitTestFactory.hitTest(
|
|
||||||
props.sceneHelper,
|
|
||||||
new THREE.Vector2(e.position.x, e.position.y),
|
|
||||||
camera,
|
|
||||||
{ tolerancePixels: 3, cameraPixelSize: e.pixelSize }
|
|
||||||
);
|
|
||||||
props.onMouseMove?.({
|
props.onMouseMove?.({
|
||||||
camera,
|
camera,
|
||||||
scene,
|
scene,
|
||||||
hitTest,
|
hitResults,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
handleClick = (e: InteractionMouseEventArgs) => {
|
handleClick = (e: InteractionMouseEventArgs) => {
|
||||||
const hitTest = HitTestFactory.hitTest(
|
const hitResults = props.sceneHelper.hitTest(
|
||||||
props.sceneHelper,
|
e.position,
|
||||||
new THREE.Vector2(e.position.x, e.position.y),
|
e.screenSize,
|
||||||
camera,
|
|
||||||
{ tolerancePixels: 3, cameraPixelSize: e.pixelSize }
|
|
||||||
);
|
);
|
||||||
props.onClick?.({
|
props.onClick?.({
|
||||||
camera,
|
camera,
|
||||||
scene,
|
scene,
|
||||||
hitTest,
|
hitResults,
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Animation loop ---
|
// --- Animation loop ---
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@ export const Viewport = function () {
|
||||||
const sceneHelper = useSceneHelper();
|
const sceneHelper = useSceneHelper();
|
||||||
|
|
||||||
function handleMouseMove(e: ThreeViewMouseEventArgs) {
|
function handleMouseMove(e: ThreeViewMouseEventArgs) {
|
||||||
state.setHitTest(e.hitTest);
|
state.setHitTest(e.hitResults);
|
||||||
|
|
||||||
sceneHelper.clear();
|
sceneHelper.clear();
|
||||||
if (e.hitTest.objects.length) {
|
if (e.hitResults.hits.length) {
|
||||||
e.hitTest.objects.forEach((obj) => {
|
e.hitResults.hits.forEach((hit) => {
|
||||||
sceneHelper.showPoint(obj.object.uuid, obj.point);
|
sceneHelper.showPoint(hit.object.uuid, hit.point);
|
||||||
})
|
})
|
||||||
// console.log(e.position);
|
// console.log(e.position);
|
||||||
// console.log(e.hitTest.objects.map((o) => o));
|
// console.log(e.hitTest.objects.map((o) => o));
|
||||||
|
|
@ -20,7 +20,7 @@ export const Viewport = function () {
|
||||||
}
|
}
|
||||||
// raycaster.setFromCamera(new THREE.Vector2(e.x, e.y), camera);
|
// raycaster.setFromCamera(new THREE.Vector2(e.x, e.y), camera);
|
||||||
// const hits = raycaster.intersectObjects(sync.meshes);
|
// const hits = raycaster.intersectObjects(sync.meshes);
|
||||||
const hoveredFaceIds = e.hitTest.objects.map(hit => hit.object.userData.faceId);
|
const hoveredFaceIds = e.hitResults.hits.map((hit) => hit.object.userData.faceId);
|
||||||
// if (hoveredFaceIds.length)
|
// if (hoveredFaceIds.length)
|
||||||
// console.log(hoveredFaceIds);
|
// console.log(hoveredFaceIds);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,11 @@ import * as THREE from 'three';
|
||||||
import { CONTAINED, ExtendedTriangle, INTERSECTED, NOT_INTERSECTED } from 'three-mesh-bvh';
|
import { CONTAINED, ExtendedTriangle, INTERSECTED, NOT_INTERSECTED } from 'three-mesh-bvh';
|
||||||
import { CircularFrustum } from './circularFrustum';
|
import { CircularFrustum } from './circularFrustum';
|
||||||
|
|
||||||
export type FrustumHitResult = {
|
export type HitResults = {
|
||||||
|
hits: HitResult[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type HitResult = {
|
||||||
object: THREE.Object3D;
|
object: THREE.Object3D;
|
||||||
point: THREE.Vector3; // world-space closest hit point
|
point: THREE.Vector3; // world-space closest hit point
|
||||||
depth: number; // depth along frustum axis
|
depth: number; // depth along frustum axis
|
||||||
|
|
@ -119,7 +123,7 @@ export class CircularFrustumIntersection {
|
||||||
public intersectMesh(
|
public intersectMesh(
|
||||||
mesh: THREE.Mesh,
|
mesh: THREE.Mesh,
|
||||||
findAll: boolean,
|
findAll: boolean,
|
||||||
): FrustumHitResult[] {
|
): HitResult[] {
|
||||||
const geometry = mesh.geometry;
|
const geometry = mesh.geometry;
|
||||||
if (!geometry)
|
if (!geometry)
|
||||||
return [];
|
return [];
|
||||||
|
|
@ -136,7 +140,7 @@ export class CircularFrustumIntersection {
|
||||||
if (this.insersectsSphere(boundingSphere) === 'NOT_INTERSECTED')
|
if (this.insersectsSphere(boundingSphere) === 'NOT_INTERSECTED')
|
||||||
return [];
|
return [];
|
||||||
|
|
||||||
const results: FrustumHitResult[] = [];
|
const results: HitResult[] = [];
|
||||||
|
|
||||||
if (!geometry.boundsTree)
|
if (!geometry.boundsTree)
|
||||||
geometry.computeBoundsTree();
|
geometry.computeBoundsTree();
|
||||||
|
|
@ -240,8 +244,8 @@ export class CircularFrustumIntersection {
|
||||||
public intersectObject(
|
public intersectObject(
|
||||||
obj: THREE.Object3D,
|
obj: THREE.Object3D,
|
||||||
options: CircularFrustumIntersectionOptions = {},
|
options: CircularFrustumIntersectionOptions = {},
|
||||||
): FrustumHitResult[] {
|
): HitResult[] {
|
||||||
const results: FrustumHitResult[] = [];
|
const results: HitResult[] = [];
|
||||||
|
|
||||||
obj.traverseVisible((object) => {
|
obj.traverseVisible((object) => {
|
||||||
if (options.filter && !options.filter(object))
|
if (options.filter && !options.filter(object))
|
||||||
|
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,7 +2,7 @@ import type { ColorRepresentation, Object3D, Object3DEventMap, OrthographicCamer
|
||||||
import { SceneSync } from "../layers/sceneSync";
|
import { SceneSync } from "../layers/sceneSync";
|
||||||
import { GeometryCache } from "../layers/geometryCache";
|
import { GeometryCache } from "../layers/geometryCache";
|
||||||
import type { Id } from "../types";
|
import type { Id } from "../types";
|
||||||
import { CircularFrustumIntersection, type FrustumHitResult } from "./circularFrustumIntersect";
|
import { CircularFrustumIntersection, type HitResult, type HitResults } from "./circularFrustumIntersect";
|
||||||
import { CircularFrustum } from "./circularFrustum";
|
import { CircularFrustum } from "./circularFrustum";
|
||||||
import './bvh';
|
import './bvh';
|
||||||
import { VolatileGeometryHelper, type VolatileGeometryOptions } from "./volatileGeometryHelper";
|
import { VolatileGeometryHelper, type VolatileGeometryOptions } from "./volatileGeometryHelper";
|
||||||
|
|
@ -30,7 +30,7 @@ export class SceneHelper {
|
||||||
public buildMouseFrustum(
|
public buildMouseFrustum(
|
||||||
mouseNormalized: Vector2Like,
|
mouseNormalized: Vector2Like,
|
||||||
screenSize: Vector2Like,
|
screenSize: Vector2Like,
|
||||||
radius: number = 15,
|
radius: number = 5,
|
||||||
): void {
|
): void {
|
||||||
if (!this.camera)
|
if (!this.camera)
|
||||||
throw new Error('Camera is not initialized');
|
throw new Error('Camera is not initialized');
|
||||||
|
|
@ -46,13 +46,16 @@ export class SceneHelper {
|
||||||
public hitTest(
|
public hitTest(
|
||||||
mouseNormalized: Vector2Like,
|
mouseNormalized: Vector2Like,
|
||||||
screenSize: Vector2Like,
|
screenSize: Vector2Like,
|
||||||
): FrustumHitResult[] {
|
): HitResults {
|
||||||
this.buildMouseFrustum(mouseNormalized, screenSize);
|
this.buildMouseFrustum(mouseNormalized, screenSize);
|
||||||
|
|
||||||
const result: FrustumHitResult[] = [];
|
const hits: HitResult[] = [];
|
||||||
for (const object of this.objects)
|
for (const object of this.objects)
|
||||||
result.push(...this.mouseFrustum.intersectObject(object));
|
hits.push(...this.mouseFrustum.intersectObject(object));
|
||||||
return result;
|
|
||||||
|
return {
|
||||||
|
hits,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public setSelection(faceIds: Id[]) {
|
public setSelection(faceIds: Id[]) {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { makeAutoObservable } from "mobx";
|
import { makeAutoObservable } from "mobx";
|
||||||
import type { Id } from "../types";
|
import type { Id } from "../types";
|
||||||
import type { HitTest } from "../helpers/hitTest";
|
import type { HitResults } from "../helpers/circularFrustumIntersect";
|
||||||
|
|
||||||
export class Root {
|
export class Root {
|
||||||
public selectedPrimitiveIds: Id[] = [];
|
public selectedPrimitiveIds: Id[] = [];
|
||||||
public hitTest: HitTest = { objects: [] };
|
public hitTest: HitResults = { hits: [] };
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
makeAutoObservable(this);
|
makeAutoObservable(this);
|
||||||
|
|
@ -14,7 +14,7 @@ export class Root {
|
||||||
this.selectedPrimitiveIds = value;
|
this.selectedPrimitiveIds = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setHitTest(value: HitTest) {
|
public setHitTest(value: HitResults) {
|
||||||
this.hitTest = value;
|
this.hitTest = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue