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)),
|
||||
|
||||
intersectsTriangle: (tri: ExtendedTriangle, _index: number, contained: boolean) => {
|
||||
// If the whole node was CONTAINED, every triangle is inside — fast path
|
||||
if (contained) {
|
||||
const worldPoint = tri.a.clone().applyMatrix4(mesh.matrixWorld);
|
||||
const depth = worldFrustum.axisNormalized.dot(worldPoint.clone().sub(worldFrustum.apex));
|
||||
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 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);
|
||||
if (d !== 'NOT_INTERSECTED') {
|
||||
if (d < bestDepth) {
|
||||
bestDepth = d;
|
||||
bestLocal = v;
|
||||
}
|
||||
if (d !== 'NOT_INTERSECTED' && (d as number) < bestDepth) {
|
||||
bestDepth = d as number;
|
||||
bestLocal = v.clone();
|
||||
}
|
||||
};
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Closest point on edge to the apex itself
|
||||
const tApex = Math.max(0, Math.min(1, -toA.dot(edge) / edge.lengthSq()));
|
||||
tryPoint(a.clone().addScaledVector(edge, tApex));
|
||||
}
|
||||
|
||||
// Also test closest point on triangle to the frustum axis ray
|
||||
const ray = new THREE.Ray(localFrustum.apex, localFrustum.axisNormalized);
|
||||
const closest = new THREE.Vector3();
|
||||
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;
|
||||
}
|
||||
}
|
||||
// 3. Closest point on the triangle face to the apex
|
||||
const closestOnFace = new THREE.Vector3();
|
||||
tri.closestPointToPoint(localFrustum.apex, closestOnFace);
|
||||
if (!isNaN(closestOnFace.x))
|
||||
tryPoint(closestOnFace);
|
||||
|
||||
if (bestLocal) {
|
||||
const worldPoint = bestLocal.clone().applyMatrix4(mesh.matrixWorld);
|
||||
if (bestLocal !== undefined) {
|
||||
const worldPoint = (bestLocal as THREE.Vector3).clone().applyMatrix4(mesh.matrixWorld);
|
||||
const worldDepth = worldFrustum.axisNormalized.dot(worldPoint.clone().sub(worldFrustum.apex));
|
||||
results.push({ object: mesh, point: worldPoint, depth: worldDepth, triangle: tri });
|
||||
return !findAll;
|
||||
|
|
|
|||
Loading…
Reference in New Issue