frustum hit test now works with faces also
This commit is contained in:
parent
c8fdeafe3f
commit
b583eaee29
|
|
@ -147,42 +147,71 @@ export class CircularFrustumIntersection {
|
||||||
intersectsBounds: (box: THREE.Box3) => intersectionResultToBvh(CircularFrustumIntersection.intersectsBox(box, localFrustum)),
|
intersectsBounds: (box: THREE.Box3) => intersectionResultToBvh(CircularFrustumIntersection.intersectsBox(box, localFrustum)),
|
||||||
|
|
||||||
intersectsTriangle: (tri: ExtendedTriangle, _index: number, contained: boolean) => {
|
intersectsTriangle: (tri: ExtendedTriangle, _index: number, contained: boolean) => {
|
||||||
// If the whole node was CONTAINED, every triangle is inside — fast path
|
|
||||||
if (contained) {
|
if (contained) {
|
||||||
const worldPoint = tri.a.clone().applyMatrix4(mesh.matrixWorld);
|
const worldPoint = tri.a.clone().applyMatrix4(mesh.matrixWorld);
|
||||||
const depth = worldFrustum.axisNormalized.dot(worldPoint.clone().sub(worldFrustum.apex));
|
const depth = worldFrustum.axisNormalized.dot(worldPoint.clone().sub(worldFrustum.apex));
|
||||||
results.push({ object: mesh, point: worldPoint, depth, triangle: tri });
|
results.push({ object: mesh, point: worldPoint, depth, triangle: tri });
|
||||||
return !findAll; // stop if we only need first hit
|
return !findAll;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test all three vertices; take the closest that's inside
|
|
||||||
let bestDepth = Infinity;
|
let bestDepth = Infinity;
|
||||||
let bestLocal: THREE.Vector3 | null = null;
|
let bestLocal: THREE.Vector3 | undefined = undefined;
|
||||||
|
|
||||||
for (const v of [tri.a, tri.b, tri.c] as THREE.Vector3[]) {
|
const tryPoint = (v: THREE.Vector3) => {
|
||||||
const d = CircularFrustumIntersection.pointAxialDepth(v, localFrustum);
|
const d = CircularFrustumIntersection.pointAxialDepth(v, localFrustum);
|
||||||
if (d !== 'NOT_INTERSECTED') {
|
if (d !== 'NOT_INTERSECTED' && (d as number) < bestDepth) {
|
||||||
if (d < bestDepth) {
|
bestDepth = d as number;
|
||||||
bestDepth = d;
|
bestLocal = v.clone();
|
||||||
bestLocal = v;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1. Test vertices
|
||||||
|
tryPoint(tri.a);
|
||||||
|
tryPoint(tri.b);
|
||||||
|
tryPoint(tri.c);
|
||||||
|
|
||||||
|
// 2. For each edge, find the point closest to the frustum axis ray,
|
||||||
|
// and also the point closest to the apex.
|
||||||
|
// This catches triangles that straddle the cone surface.
|
||||||
|
const edges: [THREE.Vector3, THREE.Vector3][] = [
|
||||||
|
[tri.a, tri.b],
|
||||||
|
[tri.b, tri.c],
|
||||||
|
[tri.c, tri.a],
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const [a, b] of edges) {
|
||||||
|
const edge = b.clone().sub(a);
|
||||||
|
const toA = a.clone().sub(localFrustum.apex);
|
||||||
|
|
||||||
|
// Closest point on edge segment to the axis ray
|
||||||
|
const edgeDir = edge.clone().normalize();
|
||||||
|
const axisDotEdge = localFrustum.axisNormalized.dot(edgeDir);
|
||||||
|
const denom = 1 - axisDotEdge * axisDotEdge;
|
||||||
|
|
||||||
|
if (Math.abs(denom) > 1e-10) {
|
||||||
|
const t = (
|
||||||
|
localFrustum.axisNormalized.dot(toA) * axisDotEdge
|
||||||
|
- toA.dot(edgeDir)
|
||||||
|
) / denom;
|
||||||
|
const edgeLen = edge.length();
|
||||||
|
const tClamped = Math.max(0, Math.min(edgeLen, t));
|
||||||
|
const pointOnEdge = a.clone().addScaledVector(edgeDir, tClamped);
|
||||||
|
tryPoint(pointOnEdge);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also test closest point on triangle to the frustum axis ray
|
// Closest point on edge to the apex itself
|
||||||
const ray = new THREE.Ray(localFrustum.apex, localFrustum.axisNormalized);
|
const tApex = Math.max(0, Math.min(1, -toA.dot(edge) / edge.lengthSq()));
|
||||||
const closest = new THREE.Vector3();
|
tryPoint(a.clone().addScaledVector(edge, tApex));
|
||||||
tri.closestPointToPoint(ray.origin, closest); // ExtendedTriangle has this
|
|
||||||
const d = CircularFrustumIntersection.pointAxialDepth(closest, localFrustum);
|
|
||||||
if (d !== 'NOT_INTERSECTED') {
|
|
||||||
if (d < bestDepth) {
|
|
||||||
bestDepth = d;
|
|
||||||
bestLocal = closest;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bestLocal) {
|
// 3. Closest point on the triangle face to the apex
|
||||||
const worldPoint = bestLocal.clone().applyMatrix4(mesh.matrixWorld);
|
const closestOnFace = new THREE.Vector3();
|
||||||
|
tri.closestPointToPoint(localFrustum.apex, closestOnFace);
|
||||||
|
if (!isNaN(closestOnFace.x))
|
||||||
|
tryPoint(closestOnFace);
|
||||||
|
|
||||||
|
if (bestLocal !== undefined) {
|
||||||
|
const worldPoint = (bestLocal as THREE.Vector3).clone().applyMatrix4(mesh.matrixWorld);
|
||||||
const worldDepth = worldFrustum.axisNormalized.dot(worldPoint.clone().sub(worldFrustum.apex));
|
const worldDepth = worldFrustum.axisNormalized.dot(worldPoint.clone().sub(worldFrustum.apex));
|
||||||
results.push({ object: mesh, point: worldPoint, depth: worldDepth, triangle: tri });
|
results.push({ object: mesh, point: worldPoint, depth: worldDepth, triangle: tri });
|
||||||
return !findAll;
|
return !findAll;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue