|
@@ -5,10 +5,6 @@ const _segCenter = /*@__PURE__*/ new Vector3();
|
|
|
const _segDir = /*@__PURE__*/ new Vector3();
|
|
const _segDir = /*@__PURE__*/ new Vector3();
|
|
|
const _diff = /*@__PURE__*/ new Vector3();
|
|
const _diff = /*@__PURE__*/ new Vector3();
|
|
|
|
|
|
|
|
-const _edge1 = /*@__PURE__*/ new Vector3();
|
|
|
|
|
-const _edge2 = /*@__PURE__*/ new Vector3();
|
|
|
|
|
-const _normal = /*@__PURE__*/ new Vector3();
|
|
|
|
|
-
|
|
|
|
|
/**
|
|
/**
|
|
|
* A ray that emits from an origin in a certain direction. The class is used by
|
|
* A ray that emits from an origin in a certain direction. The class is used by
|
|
|
* {@link Raycaster} to assist with raycasting. Raycasting is used for
|
|
* {@link Raycaster} to assist with raycasting. Raycasting is used for
|
|
@@ -539,76 +535,128 @@ class Ray {
|
|
|
*/
|
|
*/
|
|
|
intersectTriangle( a, b, c, backfaceCulling, target ) {
|
|
intersectTriangle( a, b, c, backfaceCulling, target ) {
|
|
|
|
|
|
|
|
- // Compute the offset origin, edges, and normal.
|
|
|
|
|
|
|
+ // Watertight ray/triangle intersection. Reference: Woop, Benthin, Wald,
|
|
|
|
|
+ // "Watertight Ray/Triangle Intersection", JCGT vol. 2 no. 1 (2013), Appendix A.
|
|
|
|
|
+ // https://jcgt.org/published/0002/01/05/
|
|
|
|
|
+
|
|
|
|
|
+ const origin = this.origin;
|
|
|
|
|
+ const direction = this.direction;
|
|
|
|
|
+
|
|
|
|
|
+ const dx = direction.x;
|
|
|
|
|
+ const dy = direction.y;
|
|
|
|
|
+ const dz = direction.z;
|
|
|
|
|
+
|
|
|
|
|
+ // triangle vertices relative to the ray origin
|
|
|
|
|
+
|
|
|
|
|
+ const aox = a.x - origin.x, aoy = a.y - origin.y, aoz = a.z - origin.z;
|
|
|
|
|
+ const box = b.x - origin.x, boy = b.y - origin.y, boz = b.z - origin.z;
|
|
|
|
|
+ const cox = c.x - origin.x, coy = c.y - origin.y, coz = c.z - origin.z;
|
|
|
|
|
+
|
|
|
|
|
+ // Use the dimension where the ray direction is maximal as the projection
|
|
|
|
|
+ // axis (kz) and read every component already permuted into (kx, ky, kz).
|
|
|
|
|
+ // kx and ky are swapped when the direction's kz component is negative, to
|
|
|
|
|
+ // preserve the winding order of triangles.
|
|
|
|
|
+
|
|
|
|
|
+ const adx = Math.abs( dx ), ady = Math.abs( dy ), adz = Math.abs( dz );
|
|
|
|
|
+
|
|
|
|
|
+ let dkx, dky, dkz;
|
|
|
|
|
+ let akx, aky, akz, bkx, bky, bkz, ckx, cky, ckz;
|
|
|
|
|
+
|
|
|
|
|
+ if ( adx >= ady && adx >= adz ) {
|
|
|
|
|
|
|
|
- // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h
|
|
|
|
|
|
|
+ dkz = dx; akz = aox; bkz = box; ckz = cox;
|
|
|
|
|
|
|
|
- _edge1.subVectors( b, a );
|
|
|
|
|
- _edge2.subVectors( c, a );
|
|
|
|
|
- _normal.crossVectors( _edge1, _edge2 );
|
|
|
|
|
|
|
+ if ( dx >= 0 ) {
|
|
|
|
|
|
|
|
- // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction,
|
|
|
|
|
- // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by
|
|
|
|
|
- // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2))
|
|
|
|
|
- // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q))
|
|
|
|
|
- // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N)
|
|
|
|
|
- let DdN = this.direction.dot( _normal );
|
|
|
|
|
- let sign;
|
|
|
|
|
|
|
+ dkx = dy; dky = dz;
|
|
|
|
|
+ akx = aoy; aky = aoz; bkx = boy; bky = boz; ckx = coy; cky = coz;
|
|
|
|
|
|
|
|
- if ( DdN > 0 ) {
|
|
|
|
|
|
|
+ } else {
|
|
|
|
|
+
|
|
|
|
|
+ dkx = dz; dky = dy;
|
|
|
|
|
+ akx = aoz; aky = aoy; bkx = boz; bky = boy; ckx = coz; cky = coy;
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ } else if ( ady >= adz ) {
|
|
|
|
|
|
|
|
- if ( backfaceCulling ) return null;
|
|
|
|
|
- sign = 1;
|
|
|
|
|
|
|
+ dkz = dy; akz = aoy; bkz = boy; ckz = coy;
|
|
|
|
|
|
|
|
- } else if ( DdN < 0 ) {
|
|
|
|
|
|
|
+ if ( dy >= 0 ) {
|
|
|
|
|
|
|
|
- sign = - 1;
|
|
|
|
|
- DdN = - DdN;
|
|
|
|
|
|
|
+ dkx = dz; dky = dx;
|
|
|
|
|
+ akx = aoz; aky = aox; bkx = boz; bky = box; ckx = coz; cky = cox;
|
|
|
|
|
+
|
|
|
|
|
+ } else {
|
|
|
|
|
+
|
|
|
|
|
+ dkx = dx; dky = dz;
|
|
|
|
|
+ akx = aox; aky = aoz; bkx = box; bky = boz; ckx = cox; cky = coz;
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
} else {
|
|
} else {
|
|
|
|
|
|
|
|
- return null;
|
|
|
|
|
|
|
+ dkz = dz; akz = aoz; bkz = boz; ckz = coz;
|
|
|
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if ( dz >= 0 ) {
|
|
|
|
|
|
|
|
- _diff.subVectors( this.origin, a );
|
|
|
|
|
- const DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) );
|
|
|
|
|
|
|
+ dkx = dx; dky = dy;
|
|
|
|
|
+ akx = aox; aky = aoy; bkx = box; bky = boy; ckx = cox; cky = coy;
|
|
|
|
|
|
|
|
- // b1 < 0, no intersection
|
|
|
|
|
- if ( DdQxE2 < 0 ) {
|
|
|
|
|
|
|
+ } else {
|
|
|
|
|
|
|
|
- return null;
|
|
|
|
|
|
|
+ dkx = dy; dky = dx;
|
|
|
|
|
+ akx = aoy; aky = aox; bkx = boy; bky = box; ckx = coy; cky = cox;
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) );
|
|
|
|
|
|
|
+ // a zero direction has no maximal axis and cannot intersect
|
|
|
|
|
|
|
|
- // b2 < 0, no intersection
|
|
|
|
|
- if ( DdE1xQ < 0 ) {
|
|
|
|
|
|
|
+ if ( dkz === 0 ) return null;
|
|
|
|
|
|
|
|
- return null;
|
|
|
|
|
|
|
+ // shear constants that align the ray with the +kz axis
|
|
|
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const sx = dkx / dkz, sy = dky / dkz, sz = 1 / dkz;
|
|
|
|
|
|
|
|
- // b1+b2 > 1, no intersection
|
|
|
|
|
- if ( DdQxE2 + DdE1xQ > DdN ) {
|
|
|
|
|
|
|
+ // sheared and scaled vertices
|
|
|
|
|
|
|
|
- return null;
|
|
|
|
|
|
|
+ const ax = akx - sx * akz, ay = aky - sy * akz;
|
|
|
|
|
+ const bx = bkx - sx * bkz, by = bky - sy * bkz;
|
|
|
|
|
+ const cx = ckx - sx * ckz, cy = cky - sy * ckz;
|
|
|
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // scaled barycentric coordinates (signed edge functions); the shear makes a
|
|
|
|
|
+ // shared edge evaluate identically for both adjacent triangles, so the ray
|
|
|
|
|
+ // can never fall between them
|
|
|
|
|
|
|
|
- // Line intersects triangle, check if ray does.
|
|
|
|
|
- const QdN = - sign * _diff.dot( _normal );
|
|
|
|
|
|
|
+ const u = cx * by - cy * bx;
|
|
|
|
|
+ const v = ax * cy - ay * cx;
|
|
|
|
|
+ const w = bx * ay - by * ax;
|
|
|
|
|
|
|
|
- // t < 0, no intersection
|
|
|
|
|
- if ( QdN < 0 ) {
|
|
|
|
|
|
|
+ if ( backfaceCulling ) {
|
|
|
|
|
|
|
|
- return null;
|
|
|
|
|
|
|
+ if ( u < 0 || v < 0 || w < 0 ) return null;
|
|
|
|
|
+
|
|
|
|
|
+ } else {
|
|
|
|
|
+
|
|
|
|
|
+ if ( ( u < 0 || v < 0 || w < 0 ) && ( u > 0 || v > 0 || w > 0 ) ) return null;
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Ray intersects triangle.
|
|
|
|
|
- return this.at( QdN / DdN, target );
|
|
|
|
|
|
|
+ const det = u + v + w;
|
|
|
|
|
+
|
|
|
|
|
+ // ray is co-planar with the triangle
|
|
|
|
|
+
|
|
|
|
|
+ if ( det === 0 ) return null;
|
|
|
|
|
+
|
|
|
|
|
+ // scaled hit distance; t = tScaled / det must lie in front of the origin
|
|
|
|
|
+
|
|
|
|
|
+ const tScaled = sz * ( u * akz + v * bkz + w * ckz );
|
|
|
|
|
+
|
|
|
|
|
+ if ( det > 0 ? tScaled < 0 : tScaled > 0 ) return null;
|
|
|
|
|
+
|
|
|
|
|
+ return this.at( tScaled / det, target );
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|