Buckets:
| import { | |
| Box3, | |
| MathUtils, | |
| Matrix4, | |
| Matrix3, | |
| Ray, | |
| Vector3 | |
| } from 'three'; | |
| // module scope helper variables | |
| const a = { | |
| c: null, // center | |
| u: [ new Vector3(), new Vector3(), new Vector3() ], // basis vectors | |
| e: [] // half width | |
| }; | |
| const b = { | |
| c: null, // center | |
| u: [ new Vector3(), new Vector3(), new Vector3() ], // basis vectors | |
| e: [] // half width | |
| }; | |
| const R = [[], [], []]; | |
| const AbsR = [[], [], []]; | |
| const t = []; | |
| const xAxis = new Vector3(); | |
| const yAxis = new Vector3(); | |
| const zAxis = new Vector3(); | |
| const v1 = new Vector3(); | |
| const size = new Vector3(); | |
| const closestPoint = new Vector3(); | |
| const rotationMatrix = new Matrix3(); | |
| const aabb = new Box3(); | |
| const matrix = new Matrix4(); | |
| const inverse = new Matrix4(); | |
| const localRay = new Ray(); | |
| /** | |
| * Represents an oriented bounding box (OBB) in 3D space. | |
| * | |
| * @three_import import { OBB } from 'three/addons/math/OBB.js'; | |
| */ | |
| class OBB { | |
| /** | |
| * Constructs a new OBB. | |
| * | |
| * @param {Vector3} [center] - The center of the OBB. | |
| * @param {Vector3} [halfSize] - Positive halfwidth extents of the OBB along each axis. | |
| * @param {Matrix3} [rotation] - The rotation of the OBB. | |
| */ | |
| constructor( center = new Vector3(), halfSize = new Vector3(), rotation = new Matrix3() ) { | |
| /** | |
| * The center of the OBB. | |
| * | |
| * @type {Vector3} | |
| */ | |
| this.center = center; | |
| /** | |
| * Positive halfwidth extents of the OBB along each axis. | |
| * | |
| * @type {Vector3} | |
| */ | |
| this.halfSize = halfSize; | |
| /** | |
| * The rotation of the OBB. | |
| * | |
| * @type {Matrix3} | |
| */ | |
| this.rotation = rotation; | |
| } | |
| /** | |
| * Sets the OBBs components to the given values. | |
| * | |
| * @param {Vector3} [center] - The center of the OBB. | |
| * @param {Vector3} [halfSize] - Positive halfwidth extents of the OBB along each axis. | |
| * @param {Matrix3} [rotation] - The rotation of the OBB. | |
| * @return {OBB} A reference to this OBB. | |
| */ | |
| set( center, halfSize, rotation ) { | |
| this.center = center; | |
| this.halfSize = halfSize; | |
| this.rotation = rotation; | |
| return this; | |
| } | |
| /** | |
| * Copies the values of the given OBB to this instance. | |
| * | |
| * @param {OBB} obb - The OBB to copy. | |
| * @return {OBB} A reference to this OBB. | |
| */ | |
| copy( obb ) { | |
| this.center.copy( obb.center ); | |
| this.halfSize.copy( obb.halfSize ); | |
| this.rotation.copy( obb.rotation ); | |
| return this; | |
| } | |
| /** | |
| * Returns a new OBB with copied values from this instance. | |
| * | |
| * @return {OBB} A clone of this instance. | |
| */ | |
| clone() { | |
| return new this.constructor().copy( this ); | |
| } | |
| /** | |
| * Returns the size of this OBB. | |
| * | |
| * @param {Vector3} target - The target vector that is used to store the method's result. | |
| * @return {Vector3} The size. | |
| */ | |
| getSize( target ) { | |
| return target.copy( this.halfSize ).multiplyScalar( 2 ); | |
| } | |
| /** | |
| * Clamps the given point within the bounds of this OBB. | |
| * | |
| * @param {Vector3} point - The point that should be clamped within the bounds of this OBB. | |
| * @param {Vector3} target - The target vector that is used to store the method's result. | |
| * @returns {Vector3} - The clamped point. | |
| */ | |
| clampPoint( point, target ) { | |
| // Reference: Closest Point on OBB to Point in Real-Time Collision Detection | |
| // by Christer Ericson (chapter 5.1.4) | |
| const halfSize = this.halfSize; | |
| v1.subVectors( point, this.center ); | |
| this.rotation.extractBasis( xAxis, yAxis, zAxis ); | |
| // start at the center position of the OBB | |
| target.copy( this.center ); | |
| // project the target onto the OBB axes and walk towards that point | |
| const x = MathUtils.clamp( v1.dot( xAxis ), - halfSize.x, halfSize.x ); | |
| target.add( xAxis.multiplyScalar( x ) ); | |
| const y = MathUtils.clamp( v1.dot( yAxis ), - halfSize.y, halfSize.y ); | |
| target.add( yAxis.multiplyScalar( y ) ); | |
| const z = MathUtils.clamp( v1.dot( zAxis ), - halfSize.z, halfSize.z ); | |
| target.add( zAxis.multiplyScalar( z ) ); | |
| return target; | |
| } | |
| /** | |
| * Returns `true` if the given point lies within this OBB. | |
| * | |
| * @param {Vector3} point - The point to test. | |
| * @returns {boolean} - Whether the given point lies within this OBB or not. | |
| */ | |
| 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; | |
| } | |
| /** | |
| * Returns `true` if the given AABB intersects this OBB. | |
| * | |
| * @param {Box3} box3 - The AABB to test. | |
| * @returns {boolean} - Whether the given AABB intersects this OBB or not. | |
| */ | |
| intersectsBox3( box3 ) { | |
| return this.intersectsOBB( obb.fromBox3( box3 ) ); | |
| } | |
| /** | |
| * Returns `true` if the given bounding sphere intersects this OBB. | |
| * | |
| * @param {Sphere} sphere - The bounding sphere to test. | |
| * @returns {boolean} - Whether the given bounding sphere intersects this OBB or not. | |
| */ | |
| 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 ); | |
| } | |
| /** | |
| * Returns `true` if the given OBB intersects this OBB. | |
| * | |
| * @param {OBB} obb - The OBB to test. | |
| * @param {number} [epsilon=Number.EPSILON] - A small value to prevent arithmetic errors. | |
| * @returns {boolean} - Whether the given OBB intersects this OBB or not. | |
| */ | |
| intersectsOBB( obb, epsilon = Number.EPSILON ) { | |
| // Reference: OBB-OBB Intersection in Real-Time Collision Detection | |
| // by Christer Ericson (chapter 4.4.1) | |
| // 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; | |
| } | |
| /** | |
| * Returns `true` if the given plane intersects this OBB. | |
| * | |
| * @param {Plane} plane - The plane to test. | |
| * @returns {boolean} Whether the given plane intersects this OBB or not. | |
| */ | |
| intersectsPlane( plane ) { | |
| // Reference: Testing Box Against Plane in Real-Time Collision Detection | |
| // by Christer Ericson (chapter 5.2.3) | |
| 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 | |
| * in the given 3D vector. | |
| * | |
| * @param {Ray} ray - The ray to test. | |
| * @param {Vector3} target - The target vector that is used to store the method's result. | |
| * @return {?Vector3} The intersection point. If no intersection is detected, `null` is returned. | |
| */ | |
| intersectRay( ray, target ) { | |
| // 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, target ) ) { | |
| // transform the intersection point back to world space | |
| return target.applyMatrix4( matrix ); | |
| } else { | |
| return null; | |
| } | |
| } | |
| /** | |
| * Returns `true` if the given ray intersects this OBB. | |
| * | |
| * @param {Ray} ray - The ray to test. | |
| * @returns {boolean} Whether the given ray intersects this OBB or not. | |
| */ | |
| intersectsRay( ray ) { | |
| return this.intersectRay( ray, v1 ) !== null; | |
| } | |
| /** | |
| * Defines an OBB based on the given AABB. | |
| * | |
| * @param {Box3} box3 - The AABB to setup the OBB from. | |
| * @return {OBB} A reference of this OBB. | |
| */ | |
| fromBox3( box3 ) { | |
| box3.getCenter( this.center ); | |
| box3.getSize( this.halfSize ).multiplyScalar( 0.5 ); | |
| this.rotation.identity(); | |
| return this; | |
| } | |
| /** | |
| * Returns `true` if the given OBB is equal to this OBB. | |
| * | |
| * @param {OBB} obb - The OBB to test. | |
| * @returns {boolean} Whether the given OBB is equal to this OBB or not. | |
| */ | |
| equals( obb ) { | |
| return obb.center.equals( this.center ) && | |
| obb.halfSize.equals( this.halfSize ) && | |
| obb.rotation.equals( this.rotation ); | |
| } | |
| /** | |
| * Applies the given transformation matrix to this OBB. This method can be | |
| * used to transform the bounding volume with the world matrix of a 3D object | |
| * in order to keep both entities in sync. | |
| * | |
| * @param {Matrix4} matrix - The matrix to apply. | |
| * @return {OBB} A reference of this OBB. | |
| */ | |
| 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(); | |
| export { OBB }; | |
Xet Storage Details
- Size:
- 13.9 kB
- Xet hash:
- 89bff6c2acd90beec5a1ee34a48c9aac4db5fdd9eadd6b7462b2bec6571b7342
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.