ggabcd-meshwalk
Version:
MeshWalk.js is a JS library which helps your TPS game development with three.js.
532 lines (376 loc) • 12.8 kB
JavaScript
import { THREE, onInstallHandlers } from '../install.js';
let vec3;
let vec3_0;
let vec3_1;
let center;
let extents;
onInstallHandlers.push( () => {
vec3 = new THREE.Vector3();
vec3_0 = new THREE.Vector3();
vec3_1 = new THREE.Vector3();
center = new THREE.Vector3();
extents = new THREE.Vector3();
} );
// aabb: <THREE.Box3>
// Plane: <THREE.Plane>
export function isIntersectionAABBPlane( aabb, Plane ) {
center.addVectors( aabb.max, aabb.min ).multiplyScalar( 0.5 );
extents.subVectors( aabb.max, center );
const r = extents.x * Math.abs( Plane.normal.x ) + extents.y * Math.abs( Plane.normal.y ) + extents.z * Math.abs( Plane.normal.z );
const s = Plane.normal.dot( center ) - Plane.constant;
return Math.abs( s ) <= r;
}
let v0;
let v1;
let v2;
let f0;
let f1;
let f2;
let a00;
let a01;
let a02;
let a10;
let a11;
let a12;
let a20;
let a21;
let a22;
let plane;
onInstallHandlers.push( () => {
v0 = new THREE.Vector3();
v1 = new THREE.Vector3();
v2 = new THREE.Vector3();
f0 = new THREE.Vector3();
f1 = new THREE.Vector3();
f2 = new THREE.Vector3();
a00 = new THREE.Vector3();
a01 = new THREE.Vector3();
a02 = new THREE.Vector3();
a10 = new THREE.Vector3();
a11 = new THREE.Vector3();
a12 = new THREE.Vector3();
a20 = new THREE.Vector3();
a21 = new THREE.Vector3();
a22 = new THREE.Vector3();
plane = new THREE.Plane();
} );
// based on http://www.gamedev.net/topic/534655-aabb-triangleplane-intersection--distance-to-plane-is-incorrect-i-have-solved-it/
//
// a: <THREE.Vector3>, // vertex of a triangle
// b: <THREE.Vector3>, // vertex of a triangle
// c: <THREE.Vector3>, // vertex of a triangle
// aabb: <THREE.Box3>
export function isIntersectionTriangleAABB( a, b, c, aabb ) {
let p0, p1, p2, r;
// Compute box center and extents of AABoundingBox (if not already given in that format)
center.addVectors( aabb.max, aabb.min ).multiplyScalar( 0.5 );
extents.subVectors( aabb.max, center );
// Translate triangle as conceptually moving AABB to origin
v0.subVectors( a, center );
v1.subVectors( b, center );
v2.subVectors( c, center );
// Compute edge vectors for triangle
f0.subVectors( v1, v0 );
f1.subVectors( v2, v1 );
f2.subVectors( v0, v2 );
// Test axes a00..a22 (category 3)
a00.set( 0, - f0.z, f0.y );
a01.set( 0, - f1.z, f1.y );
a02.set( 0, - f2.z, f2.y );
a10.set( f0.z, 0, - f0.x );
a11.set( f1.z, 0, - f1.x );
a12.set( f2.z, 0, - f2.x );
a20.set( - f0.y, f0.x, 0 );
a21.set( - f1.y, f1.x, 0 );
a22.set( - f2.y, f2.x, 0 );
// Test axis a00
p0 = v0.dot( a00 );
p1 = v1.dot( a00 );
p2 = v2.dot( a00 );
r = extents.y * Math.abs( f0.z ) + extents.z * Math.abs( f0.y );
if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) {
return false; // Axis is a separating axis
}
// Test axis a01
p0 = v0.dot( a01 );
p1 = v1.dot( a01 );
p2 = v2.dot( a01 );
r = extents.y * Math.abs( f1.z ) + extents.z * Math.abs( f1.y );
if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) {
return false; // Axis is a separating axis
}
// Test axis a02
p0 = v0.dot( a02 );
p1 = v1.dot( a02 );
p2 = v2.dot( a02 );
r = extents.y * Math.abs( f2.z ) + extents.z * Math.abs( f2.y );
if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) {
return false; // Axis is a separating axis
}
// Test axis a10
p0 = v0.dot( a10 );
p1 = v1.dot( a10 );
p2 = v2.dot( a10 );
r = extents.x * Math.abs( f0.z ) + extents.z * Math.abs( f0.x );
if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) {
return false; // Axis is a separating axis
}
// Test axis a11
p0 = v0.dot( a11 );
p1 = v1.dot( a11 );
p2 = v2.dot( a11 );
r = extents.x * Math.abs( f1.z ) + extents.z * Math.abs( f1.x );
if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) {
return false; // Axis is a separating axis
}
// Test axis a12
p0 = v0.dot( a12 );
p1 = v1.dot( a12 );
p2 = v2.dot( a12 );
r = extents.x * Math.abs( f2.z ) + extents.z * Math.abs( f2.x );
if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) {
return false; // Axis is a separating axis
}
// Test axis a20
p0 = v0.dot( a20 );
p1 = v1.dot( a20 );
p2 = v2.dot( a20 );
r = extents.x * Math.abs( f0.y ) + extents.y * Math.abs( f0.x );
if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) {
return false; // Axis is a separating axis
}
// Test axis a21
p0 = v0.dot( a21 );
p1 = v1.dot( a21 );
p2 = v2.dot( a21 );
r = extents.x * Math.abs( f1.y ) + extents.y * Math.abs( f1.x );
if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) {
return false; // Axis is a separating axis
}
// Test axis a22
p0 = v0.dot( a22 );
p1 = v1.dot( a22 );
p2 = v2.dot( a22 );
r = extents.x * Math.abs( f2.y ) + extents.y * Math.abs( f2.x );
if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) {
return false; // Axis is a separating axis
}
// Test the three axes corresponding to the face normals of AABB b (category 1).
// Exit if...
// ... [-extents.x, extents.x] and [min(v0.x,v1.x,v2.x), max(v0.x,v1.x,v2.x)] do not overlap
if ( Math.max( v0.x, v1.x, v2.x ) < - extents.x || Math.min( v0.x, v1.x, v2.x ) > extents.x ) {
return false;
}
// ... [-extents.y, extents.y] and [min(v0.y,v1.y,v2.y), max(v0.y,v1.y,v2.y)] do not overlap
if ( Math.max( v0.y, v1.y, v2.y ) < - extents.y || Math.min( v0.y, v1.y, v2.y ) > extents.y ) {
return false;
}
// ... [-extents.z, extents.z] and [min(v0.z,v1.z,v2.z), max(v0.z,v1.z,v2.z)] do not overlap
if ( Math.max( v0.z, v1.z, v2.z ) < - extents.z || Math.min( v0.z, v1.z, v2.z ) > extents.z ) {
return false;
}
// Test separating axis corresponding to triangle face normal (category 2)
// Face Normal is -ve as Triangle is clockwise winding (and XNA uses -z for into screen)
plane.normal.copy( f1 ).cross( f0 ).normalize();
plane.constant = plane.normal.dot( a );
return isIntersectionAABBPlane( aabb, plane );
}
// sphere1: <THREE.Sphere>
// sphere2: <THREE.Sphere>
export function isIntersectionSphereSphere( sphere1, sphere2 ) {
const radiusSum = sphere1.radius + sphere2.radius;
return sphere1.center.distanceToSquared( sphere2.center ) <= ( radiusSum * radiusSum );
}
// Section 5.1.3
// sphere: <THREE.Sphere>
// aabb: <THREE.Box3>
export function isIntersectionSphereAABB( sphere, aabb ) {
let sqDist = 0;
if ( sphere.center.x < aabb.min.x ) sqDist += ( aabb.min.x - sphere.center.x ) * ( aabb.min.x - sphere.center.x );
if ( sphere.center.x > aabb.max.x ) sqDist += ( sphere.center.x - aabb.max.x ) * ( sphere.center.x - aabb.max.x );
if ( sphere.center.y < aabb.min.y ) sqDist += ( aabb.min.y - sphere.center.y ) * ( aabb.min.y - sphere.center.y );
if ( sphere.center.y > aabb.max.y ) sqDist += ( sphere.center.y - aabb.max.y ) * ( sphere.center.y - aabb.max.y );
if ( sphere.center.z < aabb.min.z ) sqDist += ( aabb.min.z - sphere.center.z ) * ( aabb.min.z - sphere.center.z );
if ( sphere.center.z > aabb.max.z ) sqDist += ( sphere.center.z - aabb.max.z ) * ( sphere.center.z - aabb.max.z );
return sqDist <= sphere.radius * sphere.radius;
}
let A;
let B;
let C;
let V;
let AB;
let BC;
let CA;
let Q1;
let Q2;
let Q3;
let QC;
let QA;
let QB;
let negatedNormal;
onInstallHandlers.push( () => {
A = new THREE.Vector3();
B = new THREE.Vector3();
C = new THREE.Vector3();
V = new THREE.Vector3();
AB = new THREE.Vector3();
BC = new THREE.Vector3();
CA = new THREE.Vector3();
Q1 = new THREE.Vector3();
Q2 = new THREE.Vector3();
Q3 = new THREE.Vector3();
QC = new THREE.Vector3();
QA = new THREE.Vector3();
QB = new THREE.Vector3();
negatedNormal = new THREE.Vector3();
} );
//http://clb.demon.fi/MathGeoLib/docs/Triangle.cpp_code.html#459
// sphere: <THREE.Sphere>
// a: <THREE.Vector3>, // vertex of a triangle
// b: <THREE.Vector3>, // vertex of a triangle
// c: <THREE.Vector3>, // vertex of a triangle
// normal: <THREE.Vector3>, // normal of a triangle
export function isIntersectionSphereTriangle( sphere, a, b, c, normal ) {
// http://realtimecollisiondetection.net/blog/?p=103
// vs plain of triangle face
A.subVectors( a, sphere.center );
B.subVectors( b, sphere.center );
C.subVectors( c, sphere.center );
const rr = sphere.radius * sphere.radius;
V.crossVectors( vec3_0.subVectors( B, A ), vec3_1.subVectors( C, A ) );
const d = A.dot( V );
const e = V.dot( V );
if ( d * d > rr * e ) {
return false;
}
// vs triangle vertex
const aa = A.dot( A );
const ab = A.dot( B );
const ac = A.dot( C );
const bb = B.dot( B );
const bc = B.dot( C );
const cc = C.dot( C );
if (
( aa > rr ) & ( ab > aa ) & ( ac > aa ) ||
( bb > rr ) & ( ab > bb ) & ( bc > bb ) ||
( cc > rr ) & ( ac > cc ) & ( bc > cc )
) {
return false;
}
// vs edge
AB.subVectors( B, A );
BC.subVectors( C, B );
CA.subVectors( A, C );
const d1 = ab - aa;
const d2 = bc - bb;
const d3 = ac - cc;
const e1 = AB.dot( AB );
const e2 = BC.dot( BC );
const e3 = CA.dot( CA );
Q1.subVectors( A.multiplyScalar( e1 ), AB.multiplyScalar( d1 ) );
Q2.subVectors( B.multiplyScalar( e2 ), BC.multiplyScalar( d2 ) );
Q3.subVectors( C.multiplyScalar( e3 ), CA.multiplyScalar( d3 ) );
QC.subVectors( C.multiplyScalar( e1 ), Q1 );
QA.subVectors( A.multiplyScalar( e2 ), Q2 );
QB.subVectors( B.multiplyScalar( e3 ), Q3 );
if (
( Q1.dot( Q1 ) > rr * e1 * e1 ) && ( Q1.dot( QC ) >= 0 ) ||
( Q2.dot( Q2 ) > rr * e2 * e2 ) && ( Q2.dot( QA ) >= 0 ) ||
( Q3.dot( Q3 ) > rr * e3 * e3 ) && ( Q3.dot( QB ) >= 0 )
) {
return false;
}
const distance = Math.sqrt( d * d / e ) - sphere.radius - 1;
negatedNormal.set( - normal.x, - normal.y, - normal.z );
const contactPoint = sphere.center.clone().add( negatedNormal.multiplyScalar( distance ) );
return {
distance,
contactPoint,
};
}
// based on Real-Time Collision Detection Section 5.3.4
// p: <THREE.Vector3>, // line3.start
// q: <THREE.Vector3>, // line3.end
// a: <THREE.Vector3>, // triangle.a
// b: <THREE.Vector3>, // triangle.b
// c: <THREE.Vector3>, // triangle.c
// normal: <THREE.Vector3>, // triangle.normal, optional
// var scalarTriple = function ( a, b, c ) {
// var m = b.clone().cross( c );
// return a.dot( m );
// }
// var vectorTriple = function ( a, b, c ) {
// var m = b.clone().cross( c );
// return a.clone().cross( m );
// }
// export function isIntersectionLineTrianglefunction ( p, q, a, b, c, precisio{
// var pq = q.clone().sub( p ),
// pa = a.clone().sub( p ),
// pb = b.clone().sub( p ),
// pc = c.clone().sub( p ),
// u, v, w;
// u = scalarTriple( pq, pc, pb );
// if ( u < 0 ) { return false; }
// v = scalarTriple( pq, pa, pc );
// if ( v < 0 ) { return false; }
// w = scalarTriple( pq, pb, pa );
// if ( w < 0 ) { return false; }
// var denom = 1 / ( u + v + w );
// u *= denom;
// v *= denom;
// w *= denom;
// var au = a.clone().multiplyScalar( u ),
// bv = b.clone().multiplyScalar( v ),
// cw = c.clone().multiplyScalar( w ),
// contactPoint = au.clone().add( bv ).add( cw );
// return {
// contactPoint: contactPoint
// }
// }
let ab;
let ac;
let qp;
let n;
let ap;
let e;
let au;
let bv;
let cw;
onInstallHandlers.push( () => {
ab = new THREE.Vector3();
ac = new THREE.Vector3();
qp = new THREE.Vector3();
n = new THREE.Vector3();
ap = new THREE.Vector3();
e = new THREE.Vector3();
au = new THREE.Vector3();
bv = new THREE.Vector3();
cw = new THREE.Vector3();
} );
export function testSegmentTriangle( p, q, a, b, c ) {
ab.subVectors( b, a );
ac.subVectors( c, a );
qp.subVectors( p, q );
n.copy( ab ).cross( ac );
const d = qp.dot( n );
if ( d <= 0 ) return false;
ap.subVectors( p, a );
let t = ap.dot( n );
if ( t < 0 ) return 0;
if ( t > d ) return 0;
e.copy( qp ).cross( ap );
let v = ac.dot( e );
if ( v < 0 || v > d ) return 0;
let w = vec3.copy( ab ).dot( e ) * - 1;
if ( w < 0 || v + w > d ) return 0;
const ood = 1 / d;
t *= ood;
v *= ood;
w *= ood;
const u = 1 - v - w;
au.copy( a ).multiplyScalar( u );
bv.copy( b ).multiplyScalar( v );
cw.copy( c ).multiplyScalar( w );
const contactPoint = au.clone().add( bv ).add( cw );
return { contactPoint };
}