OBB.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. /**
  2. * @author Mugen87 / https://github.com/Mugen87
  3. */
  4. THREE.OBB = ( function () {
  5. var a = {
  6. c: null, // center
  7. u: [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ], // basis vectors
  8. e: [] // half width
  9. };
  10. var b = {
  11. c: null, // center
  12. u: [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ], // basis vectors
  13. e: [] // half width
  14. };
  15. var R = [[], [], []];
  16. var AbsR = [[], [], []];
  17. var t = [];
  18. var xAxis = new THREE.Vector3();
  19. var yAxis = new THREE.Vector3();
  20. var zAxis = new THREE.Vector3();
  21. var v1 = new THREE.Vector3();
  22. var closestPoint = new THREE.Vector3();
  23. var rotationMatrix = new THREE.Matrix3();
  24. //
  25. function OBB( center = new THREE.Vector3(), halfSize = new THREE.Vector3(), rotation = new THREE.Matrix3() ) {
  26. this.center = center;
  27. this.halfSize = halfSize;
  28. this.rotation = rotation;
  29. }
  30. Object.assign( OBB.prototype, {
  31. set: function ( center, halfSize, rotation ) {
  32. this.center = center;
  33. this.halfSize = halfSize;
  34. this.rotation = rotation;
  35. return this;
  36. },
  37. copy: function ( obb ) {
  38. this.center.copy( obb.center );
  39. this.halfSize.copy( obb.halfSize );
  40. this.rotation.copy( obb.rotation );
  41. return this;
  42. },
  43. clone: function () {
  44. return new this.constructor().copy( this );
  45. },
  46. getSize: function ( result ) {
  47. return result.copy( this.halfSize ).multiplyScalar( 2 );
  48. },
  49. /**
  50. * Reference: Closest Point on OBB to Point in Real-Time Collision Detection
  51. * by Christer Ericson (chapter 5.1.4)
  52. */
  53. clampPoint: function ( point, result ) {
  54. var halfSize = this.halfSize;
  55. v1.subVectors( point, this.center );
  56. this.rotation.extractBasis( xAxis, yAxis, zAxis );
  57. // start at the center position of the OBB
  58. result.copy( this.center );
  59. // project the target onto the OBB axes and walk towards that point
  60. var x = THREE.MathUtils.clamp( v1.dot( xAxis ), - halfSize.x, halfSize.x );
  61. result.add( xAxis.multiplyScalar( x ) );
  62. var y = THREE.MathUtils.clamp( v1.dot( yAxis ), - halfSize.y, halfSize.y );
  63. result.add( yAxis.multiplyScalar( y ) );
  64. var z = THREE.MathUtils.clamp( v1.dot( zAxis ), - halfSize.z, halfSize.z );
  65. result.add( zAxis.multiplyScalar( z ) );
  66. return result;
  67. },
  68. containsPoint: function ( point ) {
  69. v1.subVectors( point, this.center );
  70. this.rotation.extractBasis( xAxis, yAxis, zAxis );
  71. // project v1 onto each axis and check if these points lie inside the OBB
  72. return Math.abs( v1.dot( xAxis ) ) <= this.halfSize.x &&
  73. Math.abs( v1.dot( yAxis ) ) <= this.halfSize.y &&
  74. Math.abs( v1.dot( zAxis ) ) <= this.halfSize.z;
  75. },
  76. intersectsBox3: function ( box3 ) {
  77. return this.intersectsOBB( obb.fromBox3( box3 ) );
  78. },
  79. intersectsSphere: function ( sphere ) {
  80. // find the point on the OBB closest to the sphere center
  81. this.clampPoint( sphere.center, closestPoint );
  82. // if that point is inside the sphere, the OBB and sphere intersect
  83. return closestPoint.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius );
  84. },
  85. /**
  86. * Reference: OBB-OBB Intersection in Real-Time Collision Detection
  87. * by Christer Ericson (chapter 4.4.1)
  88. *
  89. */
  90. intersectsOBB: function ( obb, epsilon = Number.EPSILON ) {
  91. // prepare data structures (the code uses the same nomenclature like the reference)
  92. a.c = this.center;
  93. a.e[ 0 ] = this.halfSize.x;
  94. a.e[ 1 ] = this.halfSize.y;
  95. a.e[ 2 ] = this.halfSize.z;
  96. this.rotation.extractBasis( a.u[ 0 ], a.u[ 1 ], a.u[ 2 ] );
  97. b.c = obb.center;
  98. b.e[ 0 ] = obb.halfSize.x;
  99. b.e[ 1 ] = obb.halfSize.y;
  100. b.e[ 2 ] = obb.halfSize.z;
  101. obb.rotation.extractBasis( b.u[ 0 ], b.u[ 1 ], b.u[ 2 ] );
  102. // compute rotation matrix expressing b in a's coordinate frame
  103. for ( var i = 0; i < 3; i ++ ) {
  104. for ( var j = 0; j < 3; j ++ ) {
  105. R[ i ][ j ] = a.u[ i ].dot( b.u[ j ] );
  106. }
  107. }
  108. // compute translation vector
  109. v1.subVectors( b.c, a.c );
  110. // bring translation into a's coordinate frame
  111. t[ 0 ] = v1.dot( a.u[ 0 ] );
  112. t[ 1 ] = v1.dot( a.u[ 1 ] );
  113. t[ 2 ] = v1.dot( a.u[ 2 ] );
  114. // compute common subexpressions. Add in an epsilon term to
  115. // counteract arithmetic errors when two edges are parallel and
  116. // their cross product is (near) null
  117. for ( var i = 0; i < 3; i ++ ) {
  118. for ( var j = 0; j < 3; j ++ ) {
  119. AbsR[ i ][ j ] = Math.abs( R[ i ][ j ] ) + epsilon;
  120. }
  121. }
  122. var ra, rb;
  123. // test axes L = A0, L = A1, L = A2
  124. for ( var i = 0; i < 3; i ++ ) {
  125. ra = a.e[ i ];
  126. rb = b.e[ 0 ] * AbsR[ i ][ 0 ] + b.e[ 1 ] * AbsR[ i ][ 1 ] + b.e[ 2 ] * AbsR[ i ][ 2 ];
  127. if ( Math.abs( t[ i ] ) > ra + rb ) return false;
  128. }
  129. // test axes L = B0, L = B1, L = B2
  130. for ( var i = 0; i < 3; i ++ ) {
  131. ra = a.e[ 0 ] * AbsR[ 0 ][ i ] + a.e[ 1 ] * AbsR[ 1 ][ i ] + a.e[ 2 ] * AbsR[ 2 ][ i ];
  132. rb = b.e[ i ];
  133. if ( Math.abs( t[ 0 ] * R[ 0 ][ i ] + t[ 1 ] * R[ 1 ][ i ] + t[ 2 ] * R[ 2 ][ i ] ) > ra + rb ) return false;
  134. }
  135. // test axis L = A0 x B0
  136. ra = a.e[ 1 ] * AbsR[ 2 ][ 0 ] + a.e[ 2 ] * AbsR[ 1 ][ 0 ];
  137. rb = b.e[ 1 ] * AbsR[ 0 ][ 2 ] + b.e[ 2 ] * AbsR[ 0 ][ 1 ];
  138. if ( Math.abs( t[ 2 ] * R[ 1 ][ 0 ] - t[ 1 ] * R[ 2 ][ 0 ] ) > ra + rb ) return false;
  139. // test axis L = A0 x B1
  140. ra = a.e[ 1 ] * AbsR[ 2 ][ 1 ] + a.e[ 2 ] * AbsR[ 1 ][ 1 ];
  141. rb = b.e[ 0 ] * AbsR[ 0 ][ 2 ] + b.e[ 2 ] * AbsR[ 0 ][ 0 ];
  142. if ( Math.abs( t[ 2 ] * R[ 1 ][ 1 ] - t[ 1 ] * R[ 2 ][ 1 ] ) > ra + rb ) return false;
  143. // test axis L = A0 x B2
  144. ra = a.e[ 1 ] * AbsR[ 2 ][ 2 ] + a.e[ 2 ] * AbsR[ 1 ][ 2 ];
  145. rb = b.e[ 0 ] * AbsR[ 0 ][ 1 ] + b.e[ 1 ] * AbsR[ 0 ][ 0 ];
  146. if ( Math.abs( t[ 2 ] * R[ 1 ][ 2 ] - t[ 1 ] * R[ 2 ][ 2 ] ) > ra + rb ) return false;
  147. // test axis L = A1 x B0
  148. ra = a.e[ 0 ] * AbsR[ 2 ][ 0 ] + a.e[ 2 ] * AbsR[ 0 ][ 0 ];
  149. rb = b.e[ 1 ] * AbsR[ 1 ][ 2 ] + b.e[ 2 ] * AbsR[ 1 ][ 1 ];
  150. if ( Math.abs( t[ 0 ] * R[ 2 ][ 0 ] - t[ 2 ] * R[ 0 ][ 0 ] ) > ra + rb ) return false;
  151. // test axis L = A1 x B1
  152. ra = a.e[ 0 ] * AbsR[ 2 ][ 1 ] + a.e[ 2 ] * AbsR[ 0 ][ 1 ];
  153. rb = b.e[ 0 ] * AbsR[ 1 ][ 2 ] + b.e[ 2 ] * AbsR[ 1 ][ 0 ];
  154. if ( Math.abs( t[ 0 ] * R[ 2 ][ 1 ] - t[ 2 ] * R[ 0 ][ 1 ] ) > ra + rb ) return false;
  155. // test axis L = A1 x B2
  156. ra = a.e[ 0 ] * AbsR[ 2 ][ 2 ] + a.e[ 2 ] * AbsR[ 0 ][ 2 ];
  157. rb = b.e[ 0 ] * AbsR[ 1 ][ 1 ] + b.e[ 1 ] * AbsR[ 1 ][ 0 ];
  158. if ( Math.abs( t[ 0 ] * R[ 2 ][ 2 ] - t[ 2 ] * R[ 0 ][ 2 ] ) > ra + rb ) return false;
  159. // test axis L = A2 x B0
  160. ra = a.e[ 0 ] * AbsR[ 1 ][ 0 ] + a.e[ 1 ] * AbsR[ 0 ][ 0 ];
  161. rb = b.e[ 1 ] * AbsR[ 2 ][ 2 ] + b.e[ 2 ] * AbsR[ 2 ][ 1 ];
  162. if ( Math.abs( t[ 1 ] * R[ 0 ][ 0 ] - t[ 0 ] * R[ 1 ][ 0 ] ) > ra + rb ) return false;
  163. // test axis L = A2 x B1
  164. ra = a.e[ 0 ] * AbsR[ 1 ][ 1 ] + a.e[ 1 ] * AbsR[ 0 ][ 1 ];
  165. rb = b.e[ 0 ] * AbsR[ 2 ][ 2 ] + b.e[ 2 ] * AbsR[ 2 ][ 0 ];
  166. if ( Math.abs( t[ 1 ] * R[ 0 ][ 1 ] - t[ 0 ] * R[ 1 ][ 1 ] ) > ra + rb ) return false;
  167. // test axis L = A2 x B2
  168. ra = a.e[ 0 ] * AbsR[ 1 ][ 2 ] + a.e[ 1 ] * AbsR[ 0 ][ 2 ];
  169. rb = b.e[ 0 ] * AbsR[ 2 ][ 1 ] + b.e[ 1 ] * AbsR[ 2 ][ 0 ];
  170. if ( Math.abs( t[ 1 ] * R[ 0 ][ 2 ] - t[ 0 ] * R[ 1 ][ 2 ] ) > ra + rb ) return false;
  171. // since no separating axis is found, the OBBs must be intersecting
  172. return true;
  173. },
  174. /**
  175. * Reference: Testing Box Against Plane in Real-Time Collision Detection
  176. * by Christer Ericson (chapter 5.2.3)
  177. */
  178. intersectsPlane: function ( plane ) {
  179. this.rotation.extractBasis( xAxis, yAxis, zAxis );
  180. // compute the projection interval radius of this OBB onto L(t) = this->center + t * p.normal;
  181. const r = this.halfSize.x * Math.abs( plane.normal.dot( xAxis ) ) +
  182. this.halfSize.y * Math.abs( plane.normal.dot( yAxis ) ) +
  183. this.halfSize.z * Math.abs( plane.normal.dot( zAxis ) );
  184. // compute distance of the OBB's center from the plane
  185. const d = plane.normal.dot( this.center ) - plane.constant;
  186. // Intersection occurs when distance d falls within [-r,+r] interval
  187. return Math.abs( d ) <= r;
  188. },
  189. fromBox3: function ( box3 ) {
  190. box3.getCenter( this.center );
  191. box3.getSize( this.halfSize ).multiplyScalar( 0.5 );
  192. this.rotation.identity();
  193. return this;
  194. },
  195. equals: function ( obb ) {
  196. return obb.center.equals( this.center ) &&
  197. obb.halfSize.equals( this.halfSize ) &&
  198. obb.rotation.equals( this.rotation );
  199. },
  200. applyMatrix4: function ( matrix ) {
  201. var e = matrix.elements;
  202. var sx = v1.set( e[ 0 ], e[ 1 ], e[ 2 ] ).length();
  203. var sy = v1.set( e[ 4 ], e[ 5 ], e[ 6 ] ).length();
  204. var sz = v1.set( e[ 8 ], e[ 9 ], e[ 10 ] ).length();
  205. var det = matrix.determinant();
  206. if ( det < 0 ) sx = - sx;
  207. rotationMatrix.setFromMatrix4( matrix );
  208. var invSX = 1 / sx;
  209. var invSY = 1 / sy;
  210. var invSZ = 1 / sz;
  211. rotationMatrix.elements[ 0 ] *= invSX;
  212. rotationMatrix.elements[ 1 ] *= invSX;
  213. rotationMatrix.elements[ 2 ] *= invSX;
  214. rotationMatrix.elements[ 3 ] *= invSY;
  215. rotationMatrix.elements[ 4 ] *= invSY;
  216. rotationMatrix.elements[ 5 ] *= invSY;
  217. rotationMatrix.elements[ 6 ] *= invSZ;
  218. rotationMatrix.elements[ 7 ] *= invSZ;
  219. rotationMatrix.elements[ 8 ] *= invSZ;
  220. this.rotation.multiply( rotationMatrix );
  221. this.halfSize.x *= sx;
  222. this.halfSize.y *= sy;
  223. this.halfSize.z *= sz;
  224. v1.setFromMatrixPosition( matrix );
  225. this.center.add( v1 );
  226. return this;
  227. }
  228. } );
  229. var obb = new OBB();
  230. return OBB;
  231. } )();