frustum instead of barycentric hittest for vertices and edges
This commit is contained in:
parent
f5f5dcd84f
commit
aeef84b708
|
|
@ -85,74 +85,6 @@ export function intersectionResultToBvh(value: IntersectionResult): typeof NOT_I
|
|||
}
|
||||
}
|
||||
|
||||
const BARYCENTRIC_EPSILON = 1e-1;
|
||||
|
||||
function triangleFaceEdgeVertexHit(
|
||||
point: THREE.Vector3,
|
||||
tri: THREE.Triangle,
|
||||
// vertexIds?: [Id, Id, Id],
|
||||
): TriangleHitDetail[] {
|
||||
const results: TriangleHitDetail[] = [{ kind: 'face' }];
|
||||
|
||||
const bary = new THREE.Vector3();
|
||||
tri.getBarycoord(point, bary);
|
||||
const [u, v, w] = bary.toArray(); // x = AB, y = AC, z = BC
|
||||
|
||||
//TODO if AB is much longer than AC, epsilon has different world size. need to scale
|
||||
const eps = 1 - BARYCENTRIC_EPSILON;
|
||||
|
||||
const onAB = w < BARYCENTRIC_EPSILON;
|
||||
const onBC = u < BARYCENTRIC_EPSILON;
|
||||
const onCA = v < BARYCENTRIC_EPSILON;
|
||||
|
||||
if (onAB) {
|
||||
results.unshift({
|
||||
kind: 'edge',
|
||||
index: 0,
|
||||
ptIndexA: 0,
|
||||
ptIndexB: 1,
|
||||
ptA: tri.a,
|
||||
ptB: tri.b,
|
||||
// idA: vertexIds?.[0],
|
||||
// idB: vertexIds?.[1],
|
||||
});
|
||||
}
|
||||
if (onBC) {
|
||||
results.unshift({
|
||||
kind: 'edge',
|
||||
index: 1,
|
||||
ptIndexA: 1,
|
||||
ptIndexB: 2,
|
||||
ptA: tri.b,
|
||||
ptB: tri.c,
|
||||
// idA: vertexIds?.[1],
|
||||
// idB: vertexIds?.[2],
|
||||
});
|
||||
}
|
||||
if (onCA) {
|
||||
results.unshift({
|
||||
kind: 'edge',
|
||||
index: 2,
|
||||
ptIndexA: 2,
|
||||
ptIndexB: 0,
|
||||
ptA: tri.c,
|
||||
ptB: tri.a,
|
||||
// idA: vertexIds?.[2],
|
||||
// idB: vertexIds?.[0],
|
||||
});
|
||||
}
|
||||
|
||||
const onA = u > eps;
|
||||
const onB = v > eps;
|
||||
const onC = w > eps;
|
||||
|
||||
if (onA) results.unshift({ kind: 'vertex', index: 0, pt: tri.a });
|
||||
if (onB) results.unshift({ kind: 'vertex', index: 1, pt: tri.b });
|
||||
if (onC) results.unshift({ kind: 'vertex', index: 2, pt: tri.c });
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
function closestPointOnEdgeToRay(
|
||||
start: THREE.Vector3,
|
||||
end: THREE.Vector3,
|
||||
|
|
@ -174,6 +106,64 @@ function closestPointOnEdgeToRay(
|
|||
return start.clone().addScaledVector(edgeDir, clamp(t, 0, edgeLen) / edgeLen);
|
||||
}
|
||||
|
||||
function triangleDetailsByFrustum(
|
||||
tri: THREE.Triangle,
|
||||
localFrustum: CircularFrustum,
|
||||
): TriangleHitDetail[] {
|
||||
const results: TriangleHitDetail[] = [{ kind: 'face' }];
|
||||
|
||||
const verts: [THREE.Vector3, 0 | 1 | 2][] = [
|
||||
[tri.a, 0],
|
||||
[tri.b, 1],
|
||||
[tri.c, 2],
|
||||
];
|
||||
const edges: [THREE.Vector3, THREE.Vector3, 0 | 1 | 2, 0 | 1 | 2, 0 | 1 | 2][] = [
|
||||
[tri.a, tri.b, 0, 0, 1],
|
||||
[tri.b, tri.c, 1, 1, 2],
|
||||
[tri.c, tri.a, 2, 2, 0],
|
||||
];
|
||||
|
||||
// A vertex is "in the frustum" if it passes the cone test
|
||||
const vertexInFrustum = verts.map(([v]) =>
|
||||
CircularFrustumIntersection.pointAxialDepth(v, localFrustum) !== 'NOT_INTERSECTED'
|
||||
);
|
||||
|
||||
// Promote to vertex hits
|
||||
for (const [v, idx] of verts) {
|
||||
if (vertexInFrustum[idx]) {
|
||||
results.unshift({ kind: 'vertex', index: idx, pt: v });
|
||||
}
|
||||
}
|
||||
|
||||
// Promote to edge hits: an edge is hit if ANY point along it falls inside the frustum.
|
||||
// We sample: the two endpoints, the closest point to the axis ray, and the closest to the apex.
|
||||
for (const [a, b, edgeIdx, ptIndexA, ptIndexB] of edges) {
|
||||
if (vertexInFrustum[ptIndexA] || vertexInFrustum[ptIndexB]) {
|
||||
// At least one endpoint inside — edge is hit
|
||||
results.unshift({ kind: 'edge', index: edgeIdx, ptIndexA, ptIndexB, ptA: a, ptB: b });
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check closest point on edge to frustum axis
|
||||
const closestToAxis = closestPointOnEdgeToRay(a, b, localFrustum.ray);
|
||||
if (CircularFrustumIntersection.pointAxialDepth(closestToAxis, localFrustum) !== 'NOT_INTERSECTED') {
|
||||
results.unshift({ kind: 'edge', index: edgeIdx, ptIndexA, ptIndexB, ptA: a, ptB: b });
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check closest point on edge to apex
|
||||
const edge = b.clone().sub(a);
|
||||
const toA = a.clone().sub(localFrustum.apex);
|
||||
const tApex = clamp(-toA.dot(edge) / edge.lengthSq(), 0, 1);
|
||||
const closestToApex = a.clone().addScaledVector(edge, tApex);
|
||||
if (CircularFrustumIntersection.pointAxialDepth(closestToApex, localFrustum) !== 'NOT_INTERSECTED') {
|
||||
results.unshift({ kind: 'edge', index: edgeIdx, ptIndexA, ptIndexB, ptA: a, ptB: b });
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
export class CircularFrustumIntersection {
|
||||
public readonly frustum: CircularFrustum;
|
||||
|
||||
|
|
@ -353,7 +343,7 @@ export class CircularFrustumIntersection {
|
|||
const worldPoint = closestContained.clone().applyMatrix4(mesh.matrixWorld);
|
||||
const depth = worldFrustum.axisNormalized.dot(worldPoint.clone().sub(worldFrustum.apex));
|
||||
results.push(
|
||||
...triangleFaceEdgeVertexHit(closestContained, tri)
|
||||
...triangleDetailsByFrustum(tri, localFrustum)
|
||||
.map((details) => {
|
||||
const closestPoint = getHitClosestPoint(tri, details, worldPoint);
|
||||
const radialDistanceAbsolute = CircularFrustumIntersection.distanceToPoint(closestPoint, worldFrustum);
|
||||
|
|
@ -417,7 +407,7 @@ export class CircularFrustumIntersection {
|
|||
const worldPoint = bestPoint.local.clone().applyMatrix4(mesh.matrixWorld);
|
||||
const worldDepth = worldFrustum.axisNormalized.dot(worldPoint.clone().sub(worldFrustum.apex));
|
||||
results.push(
|
||||
...triangleFaceEdgeVertexHit(bestPoint.local, tri)
|
||||
...triangleDetailsByFrustum(tri, localFrustum)
|
||||
.map((details) => {
|
||||
const closestPoint = getHitClosestPoint(tri, details, worldPoint);
|
||||
const radialDistanceAbsolute = CircularFrustumIntersection.distanceToPoint(closestPoint, worldFrustum);
|
||||
|
|
|
|||
Loading…
Reference in New Issue