Dies ist das Repository meines kleinen Portfolios.
Im Hintergrund läuft eine Planetensimulation, geschrieben in JavaScript und Three.js.
Die zu sehenden Texturen stammen von:
https://www.solarsystemscope.com/textures/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
341 lines
10 KiB
341 lines
10 KiB
( function () { |
|
|
|
const a = { |
|
c: null, |
|
// center |
|
u: [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ], |
|
// basis vectors |
|
e: [] // half width |
|
|
|
}; |
|
const b = { |
|
c: null, |
|
// center |
|
u: [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ], |
|
// basis vectors |
|
e: [] // half width |
|
|
|
}; |
|
const R = [[], [], []]; |
|
const AbsR = [[], [], []]; |
|
const t = []; |
|
const xAxis = new THREE.Vector3(); |
|
const yAxis = new THREE.Vector3(); |
|
const zAxis = new THREE.Vector3(); |
|
const v1 = new THREE.Vector3(); |
|
const size = new THREE.Vector3(); |
|
const closestPoint = new THREE.Vector3(); |
|
const rotationMatrix = new THREE.Matrix3(); |
|
const aabb = new THREE.Box3(); |
|
const matrix = new THREE.Matrix4(); |
|
const inverse = new THREE.Matrix4(); |
|
const localRay = new THREE.Ray(); // OBB |
|
|
|
class OBB { |
|
|
|
constructor( center = new THREE.Vector3(), halfSize = new THREE.Vector3(), rotation = new THREE.Matrix3() ) { |
|
|
|
this.center = center; |
|
this.halfSize = halfSize; |
|
this.rotation = rotation; |
|
|
|
} |
|
|
|
set( center, halfSize, rotation ) { |
|
|
|
this.center = center; |
|
this.halfSize = halfSize; |
|
this.rotation = rotation; |
|
return this; |
|
|
|
} |
|
|
|
copy( obb ) { |
|
|
|
this.center.copy( obb.center ); |
|
this.halfSize.copy( obb.halfSize ); |
|
this.rotation.copy( obb.rotation ); |
|
return this; |
|
|
|
} |
|
|
|
clone() { |
|
|
|
return new this.constructor().copy( this ); |
|
|
|
} |
|
|
|
getSize( result ) { |
|
|
|
return result.copy( this.halfSize ).multiplyScalar( 2 ); |
|
|
|
} |
|
/** |
|
* Reference: Closest Point on OBB to Point in Real-Time Collision Detection |
|
* by Christer Ericson (chapter 5.1.4) |
|
*/ |
|
|
|
|
|
clampPoint( point, result ) { |
|
|
|
const halfSize = this.halfSize; |
|
v1.subVectors( point, this.center ); |
|
this.rotation.extractBasis( xAxis, yAxis, zAxis ); // start at the center position of the OBB |
|
|
|
result.copy( this.center ); // project the target onto the OBB axes and walk towards that point |
|
|
|
const x = THREE.MathUtils.clamp( v1.dot( xAxis ), - halfSize.x, halfSize.x ); |
|
result.add( xAxis.multiplyScalar( x ) ); |
|
const y = THREE.MathUtils.clamp( v1.dot( yAxis ), - halfSize.y, halfSize.y ); |
|
result.add( yAxis.multiplyScalar( y ) ); |
|
const z = THREE.MathUtils.clamp( v1.dot( zAxis ), - halfSize.z, halfSize.z ); |
|
result.add( zAxis.multiplyScalar( z ) ); |
|
return result; |
|
|
|
} |
|
|
|
containsPoint( point ) { |
|
|
|
v1.subVectors( point, this.center ); |
|
this.rotation.extractBasis( xAxis, yAxis, zAxis ); // project v1 onto each axis and check if these points lie inside the OBB |
|
|
|
return Math.abs( v1.dot( xAxis ) ) <= this.halfSize.x && Math.abs( v1.dot( yAxis ) ) <= this.halfSize.y && Math.abs( v1.dot( zAxis ) ) <= this.halfSize.z; |
|
|
|
} |
|
|
|
intersectsBox3( box3 ) { |
|
|
|
return this.intersectsOBB( obb.fromBox3( box3 ) ); |
|
|
|
} |
|
|
|
intersectsSphere( sphere ) { |
|
|
|
// find the point on the OBB closest to the sphere center |
|
this.clampPoint( sphere.center, closestPoint ); // if that point is inside the sphere, the OBB and sphere intersect |
|
|
|
return closestPoint.distanceToSquared( sphere.center ) <= sphere.radius * sphere.radius; |
|
|
|
} |
|
/** |
|
* Reference: OBB-OBB Intersection in Real-Time Collision Detection |
|
* by Christer Ericson (chapter 4.4.1) |
|
* |
|
*/ |
|
|
|
|
|
intersectsOBB( obb, epsilon = Number.EPSILON ) { |
|
|
|
// prepare data structures (the code uses the same nomenclature like the reference) |
|
a.c = this.center; |
|
a.e[ 0 ] = this.halfSize.x; |
|
a.e[ 1 ] = this.halfSize.y; |
|
a.e[ 2 ] = this.halfSize.z; |
|
this.rotation.extractBasis( a.u[ 0 ], a.u[ 1 ], a.u[ 2 ] ); |
|
b.c = obb.center; |
|
b.e[ 0 ] = obb.halfSize.x; |
|
b.e[ 1 ] = obb.halfSize.y; |
|
b.e[ 2 ] = obb.halfSize.z; |
|
obb.rotation.extractBasis( b.u[ 0 ], b.u[ 1 ], b.u[ 2 ] ); // compute rotation matrix expressing b in a's coordinate frame |
|
|
|
for ( let i = 0; i < 3; i ++ ) { |
|
|
|
for ( let j = 0; j < 3; j ++ ) { |
|
|
|
R[ i ][ j ] = a.u[ i ].dot( b.u[ j ] ); |
|
|
|
} |
|
|
|
} // compute translation vector |
|
|
|
|
|
v1.subVectors( b.c, a.c ); // bring translation into a's coordinate frame |
|
|
|
t[ 0 ] = v1.dot( a.u[ 0 ] ); |
|
t[ 1 ] = v1.dot( a.u[ 1 ] ); |
|
t[ 2 ] = v1.dot( a.u[ 2 ] ); // compute common subexpressions. Add in an epsilon term to |
|
// counteract arithmetic errors when two edges are parallel and |
|
// their cross product is (near) null |
|
|
|
for ( let i = 0; i < 3; i ++ ) { |
|
|
|
for ( let j = 0; j < 3; j ++ ) { |
|
|
|
AbsR[ i ][ j ] = Math.abs( R[ i ][ j ] ) + epsilon; |
|
|
|
} |
|
|
|
} |
|
|
|
let ra, rb; // test axes L = A0, L = A1, L = A2 |
|
|
|
for ( let i = 0; i < 3; i ++ ) { |
|
|
|
ra = a.e[ i ]; |
|
rb = b.e[ 0 ] * AbsR[ i ][ 0 ] + b.e[ 1 ] * AbsR[ i ][ 1 ] + b.e[ 2 ] * AbsR[ i ][ 2 ]; |
|
if ( Math.abs( t[ i ] ) > ra + rb ) return false; |
|
|
|
} // test axes L = B0, L = B1, L = B2 |
|
|
|
|
|
for ( let i = 0; i < 3; i ++ ) { |
|
|
|
ra = a.e[ 0 ] * AbsR[ 0 ][ i ] + a.e[ 1 ] * AbsR[ 1 ][ i ] + a.e[ 2 ] * AbsR[ 2 ][ i ]; |
|
rb = b.e[ i ]; |
|
if ( Math.abs( t[ 0 ] * R[ 0 ][ i ] + t[ 1 ] * R[ 1 ][ i ] + t[ 2 ] * R[ 2 ][ i ] ) > ra + rb ) return false; |
|
|
|
} // test axis L = A0 x B0 |
|
|
|
|
|
ra = a.e[ 1 ] * AbsR[ 2 ][ 0 ] + a.e[ 2 ] * AbsR[ 1 ][ 0 ]; |
|
rb = b.e[ 1 ] * AbsR[ 0 ][ 2 ] + b.e[ 2 ] * AbsR[ 0 ][ 1 ]; |
|
if ( Math.abs( t[ 2 ] * R[ 1 ][ 0 ] - t[ 1 ] * R[ 2 ][ 0 ] ) > ra + rb ) return false; // test axis L = A0 x B1 |
|
|
|
ra = a.e[ 1 ] * AbsR[ 2 ][ 1 ] + a.e[ 2 ] * AbsR[ 1 ][ 1 ]; |
|
rb = b.e[ 0 ] * AbsR[ 0 ][ 2 ] + b.e[ 2 ] * AbsR[ 0 ][ 0 ]; |
|
if ( Math.abs( t[ 2 ] * R[ 1 ][ 1 ] - t[ 1 ] * R[ 2 ][ 1 ] ) > ra + rb ) return false; // test axis L = A0 x B2 |
|
|
|
ra = a.e[ 1 ] * AbsR[ 2 ][ 2 ] + a.e[ 2 ] * AbsR[ 1 ][ 2 ]; |
|
rb = b.e[ 0 ] * AbsR[ 0 ][ 1 ] + b.e[ 1 ] * AbsR[ 0 ][ 0 ]; |
|
if ( Math.abs( t[ 2 ] * R[ 1 ][ 2 ] - t[ 1 ] * R[ 2 ][ 2 ] ) > ra + rb ) return false; // test axis L = A1 x B0 |
|
|
|
ra = a.e[ 0 ] * AbsR[ 2 ][ 0 ] + a.e[ 2 ] * AbsR[ 0 ][ 0 ]; |
|
rb = b.e[ 1 ] * AbsR[ 1 ][ 2 ] + b.e[ 2 ] * AbsR[ 1 ][ 1 ]; |
|
if ( Math.abs( t[ 0 ] * R[ 2 ][ 0 ] - t[ 2 ] * R[ 0 ][ 0 ] ) > ra + rb ) return false; // test axis L = A1 x B1 |
|
|
|
ra = a.e[ 0 ] * AbsR[ 2 ][ 1 ] + a.e[ 2 ] * AbsR[ 0 ][ 1 ]; |
|
rb = b.e[ 0 ] * AbsR[ 1 ][ 2 ] + b.e[ 2 ] * AbsR[ 1 ][ 0 ]; |
|
if ( Math.abs( t[ 0 ] * R[ 2 ][ 1 ] - t[ 2 ] * R[ 0 ][ 1 ] ) > ra + rb ) return false; // test axis L = A1 x B2 |
|
|
|
ra = a.e[ 0 ] * AbsR[ 2 ][ 2 ] + a.e[ 2 ] * AbsR[ 0 ][ 2 ]; |
|
rb = b.e[ 0 ] * AbsR[ 1 ][ 1 ] + b.e[ 1 ] * AbsR[ 1 ][ 0 ]; |
|
if ( Math.abs( t[ 0 ] * R[ 2 ][ 2 ] - t[ 2 ] * R[ 0 ][ 2 ] ) > ra + rb ) return false; // test axis L = A2 x B0 |
|
|
|
ra = a.e[ 0 ] * AbsR[ 1 ][ 0 ] + a.e[ 1 ] * AbsR[ 0 ][ 0 ]; |
|
rb = b.e[ 1 ] * AbsR[ 2 ][ 2 ] + b.e[ 2 ] * AbsR[ 2 ][ 1 ]; |
|
if ( Math.abs( t[ 1 ] * R[ 0 ][ 0 ] - t[ 0 ] * R[ 1 ][ 0 ] ) > ra + rb ) return false; // test axis L = A2 x B1 |
|
|
|
ra = a.e[ 0 ] * AbsR[ 1 ][ 1 ] + a.e[ 1 ] * AbsR[ 0 ][ 1 ]; |
|
rb = b.e[ 0 ] * AbsR[ 2 ][ 2 ] + b.e[ 2 ] * AbsR[ 2 ][ 0 ]; |
|
if ( Math.abs( t[ 1 ] * R[ 0 ][ 1 ] - t[ 0 ] * R[ 1 ][ 1 ] ) > ra + rb ) return false; // test axis L = A2 x B2 |
|
|
|
ra = a.e[ 0 ] * AbsR[ 1 ][ 2 ] + a.e[ 1 ] * AbsR[ 0 ][ 2 ]; |
|
rb = b.e[ 0 ] * AbsR[ 2 ][ 1 ] + b.e[ 1 ] * AbsR[ 2 ][ 0 ]; |
|
if ( Math.abs( t[ 1 ] * R[ 0 ][ 2 ] - t[ 0 ] * R[ 1 ][ 2 ] ) > ra + rb ) return false; // since no separating axis is found, the OBBs must be intersecting |
|
|
|
return true; |
|
|
|
} |
|
/** |
|
* Reference: Testing Box Against Plane in Real-Time Collision Detection |
|
* by Christer Ericson (chapter 5.2.3) |
|
*/ |
|
|
|
|
|
intersectsPlane( plane ) { |
|
|
|
this.rotation.extractBasis( xAxis, yAxis, zAxis ); // compute the projection interval radius of this OBB onto L(t) = this->center + t * p.normal; |
|
|
|
const r = this.halfSize.x * Math.abs( plane.normal.dot( xAxis ) ) + this.halfSize.y * Math.abs( plane.normal.dot( yAxis ) ) + this.halfSize.z * Math.abs( plane.normal.dot( zAxis ) ); // compute distance of the OBB's center from the plane |
|
|
|
const d = plane.normal.dot( this.center ) - plane.constant; // Intersection occurs when distance d falls within [-r,+r] interval |
|
|
|
return Math.abs( d ) <= r; |
|
|
|
} |
|
/** |
|
* Performs a ray/OBB intersection test and stores the intersection point |
|
* to the given 3D vector. If no intersection is detected, *null* is returned. |
|
*/ |
|
|
|
|
|
intersectRay( ray, result ) { |
|
|
|
// the idea is to perform the intersection test in the local space |
|
// of the OBB. |
|
this.getSize( size ); |
|
aabb.setFromCenterAndSize( v1.set( 0, 0, 0 ), size ); // create a 4x4 transformation matrix |
|
|
|
matrix.setFromMatrix3( this.rotation ); |
|
matrix.setPosition( this.center ); // transform ray to the local space of the OBB |
|
|
|
inverse.copy( matrix ).invert(); |
|
localRay.copy( ray ).applyMatrix4( inverse ); // perform ray <-> AABB intersection test |
|
|
|
if ( localRay.intersectBox( aabb, result ) ) { |
|
|
|
// transform the intersection point back to world space |
|
return result.applyMatrix4( matrix ); |
|
|
|
} else { |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
/** |
|
* Performs a ray/OBB intersection test. Returns either true or false if |
|
* there is a intersection or not. |
|
*/ |
|
|
|
|
|
intersectsRay( ray ) { |
|
|
|
return this.intersectRay( ray, v1 ) !== null; |
|
|
|
} |
|
|
|
fromBox3( box3 ) { |
|
|
|
box3.getCenter( this.center ); |
|
box3.getSize( this.halfSize ).multiplyScalar( 0.5 ); |
|
this.rotation.identity(); |
|
return this; |
|
|
|
} |
|
|
|
equals( obb ) { |
|
|
|
return obb.center.equals( this.center ) && obb.halfSize.equals( this.halfSize ) && obb.rotation.equals( this.rotation ); |
|
|
|
} |
|
|
|
applyMatrix4( matrix ) { |
|
|
|
const e = matrix.elements; |
|
let sx = v1.set( e[ 0 ], e[ 1 ], e[ 2 ] ).length(); |
|
const sy = v1.set( e[ 4 ], e[ 5 ], e[ 6 ] ).length(); |
|
const sz = v1.set( e[ 8 ], e[ 9 ], e[ 10 ] ).length(); |
|
const det = matrix.determinant(); |
|
if ( det < 0 ) sx = - sx; |
|
rotationMatrix.setFromMatrix4( matrix ); |
|
const invSX = 1 / sx; |
|
const invSY = 1 / sy; |
|
const invSZ = 1 / sz; |
|
rotationMatrix.elements[ 0 ] *= invSX; |
|
rotationMatrix.elements[ 1 ] *= invSX; |
|
rotationMatrix.elements[ 2 ] *= invSX; |
|
rotationMatrix.elements[ 3 ] *= invSY; |
|
rotationMatrix.elements[ 4 ] *= invSY; |
|
rotationMatrix.elements[ 5 ] *= invSY; |
|
rotationMatrix.elements[ 6 ] *= invSZ; |
|
rotationMatrix.elements[ 7 ] *= invSZ; |
|
rotationMatrix.elements[ 8 ] *= invSZ; |
|
this.rotation.multiply( rotationMatrix ); |
|
this.halfSize.x *= sx; |
|
this.halfSize.y *= sy; |
|
this.halfSize.z *= sz; |
|
v1.setFromMatrixPosition( matrix ); |
|
this.center.add( v1 ); |
|
return this; |
|
|
|
} |
|
|
|
} |
|
|
|
const obb = new OBB(); |
|
|
|
THREE.OBB = OBB; |
|
|
|
} )();
|
|
|