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(
|
function closestPointOnEdgeToRay(
|
||||||
start: THREE.Vector3,
|
start: THREE.Vector3,
|
||||||
end: THREE.Vector3,
|
end: THREE.Vector3,
|
||||||
|
|
@ -174,6 +106,64 @@ function closestPointOnEdgeToRay(
|
||||||
return start.clone().addScaledVector(edgeDir, clamp(t, 0, edgeLen) / edgeLen);
|
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 {
|
export class CircularFrustumIntersection {
|
||||||
public readonly frustum: CircularFrustum;
|
public readonly frustum: CircularFrustum;
|
||||||
|
|
||||||
|
|
@ -353,7 +343,7 @@ export class CircularFrustumIntersection {
|
||||||
const worldPoint = closestContained.clone().applyMatrix4(mesh.matrixWorld);
|
const worldPoint = closestContained.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(
|
results.push(
|
||||||
...triangleFaceEdgeVertexHit(closestContained, tri)
|
...triangleDetailsByFrustum(tri, localFrustum)
|
||||||
.map((details) => {
|
.map((details) => {
|
||||||
const closestPoint = getHitClosestPoint(tri, details, worldPoint);
|
const closestPoint = getHitClosestPoint(tri, details, worldPoint);
|
||||||
const radialDistanceAbsolute = CircularFrustumIntersection.distanceToPoint(closestPoint, worldFrustum);
|
const radialDistanceAbsolute = CircularFrustumIntersection.distanceToPoint(closestPoint, worldFrustum);
|
||||||
|
|
@ -417,7 +407,7 @@ export class CircularFrustumIntersection {
|
||||||
const worldPoint = bestPoint.local.clone().applyMatrix4(mesh.matrixWorld);
|
const worldPoint = bestPoint.local.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(
|
results.push(
|
||||||
...triangleFaceEdgeVertexHit(bestPoint.local, tri)
|
...triangleDetailsByFrustum(tri, localFrustum)
|
||||||
.map((details) => {
|
.map((details) => {
|
||||||
const closestPoint = getHitClosestPoint(tri, details, worldPoint);
|
const closestPoint = getHitClosestPoint(tri, details, worldPoint);
|
||||||
const radialDistanceAbsolute = CircularFrustumIntersection.distanceToPoint(closestPoint, worldFrustum);
|
const radialDistanceAbsolute = CircularFrustumIntersection.distanceToPoint(closestPoint, worldFrustum);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue