API Docs for: GoblinPhysics
Show:

File: src\classes\Collision\GjkEpa2.js

  1. /**
  2. * Provides the classes and algorithms for running GJK+EPA based collision detection
  3. *
  4. * @class GjkEpa2
  5. * @static
  6. */
  7. Goblin.GjkEpa2 = {
  8. max_iterations: 20,
  9. epa_condition: 0.001,
  10.  
  11. /**
  12. * Holds a point on the edge of a Minkowski difference along with that point's witnesses and the direction used to find the point
  13. *
  14. * @class SupportPoint
  15. * @param witness_a {vec3} Point in first object used to find the supporting point
  16. * @param witness_b {vec3} Point in the second object ued to find th supporting point
  17. * @param point {vec3} The support point on the edge of the Minkowski difference
  18. * @constructor
  19. */
  20. SupportPoint: function( witness_a, witness_b, point ) {
  21. this.witness_a = witness_a;
  22. this.witness_b = witness_b;
  23. this.point = point;
  24. },
  25.  
  26. /**
  27. * Finds the extant point on the edge of the Minkowski difference for `object_a` - `object_b` in `direction`
  28. *
  29. * @method findSupportPoint
  30. * @param object_a {Goblin.RigidBody} First object in the search
  31. * @param object_b {Goblin.RigidBody} Second object in the search
  32. * @param direction {vec3} Direction to find the extant point in
  33. * @param gjk_point {Goblin.GjkEpa.SupportPoint} `SupportPoint` class to store the resulting point & witnesses in
  34. */
  35. findSupportPoint: (function(){
  36. var temp = new Goblin.Vector3();
  37. return function( object_a, object_b, direction, support_point ) {
  38. // Find witnesses from the objects
  39. object_a.findSupportPoint( direction, support_point.witness_a );
  40. temp.scaleVector( direction, -1 );
  41. object_b.findSupportPoint( temp, support_point.witness_b );
  42.  
  43. // Find the CSO support point
  44. support_point.point.subtractVectors( support_point.witness_a, support_point.witness_b );
  45. };
  46. })(),
  47.  
  48. /**
  49. * Perform GJK algorithm against two objects. Returns a ContactDetails object if there is a collision, else null
  50. *
  51. * @method GJK
  52. * @param object_a {Goblin.RigidBody}
  53. * @param object_b {Goblin.RigidBody}
  54. * @return {Goblin.ContactDetails|Boolean} Returns `null` if no collision, else a `ContactDetails` object
  55. */
  56. GJK: (function(){
  57. return function( object_a, object_b ) {
  58. var simplex = new Goblin.GjkEpa2.Simplex( object_a, object_b ),
  59. last_point;
  60.  
  61. while ( ( last_point = simplex.addPoint() ) ){}
  62.  
  63. // If last_point is false then there is no collision
  64. if ( last_point === false ) {
  65. Goblin.GjkEpa2.freeSimplex( simplex );
  66. return null;
  67. }
  68.  
  69. return simplex;
  70. };
  71. })(),
  72.  
  73. freeSimplex: function( simplex ) {
  74. // Free the support points used by this simplex
  75. for ( var i = 0, points_length = simplex.points.length; i < points_length; i++ ) {
  76. Goblin.ObjectPool.freeObject( 'GJK2SupportPoint', simplex.points[i] );
  77. }
  78. },
  79.  
  80. freePolyhedron: function( polyhedron ) {
  81. // Free the support points used by the polyhedron (includes the points from the simplex used to create the polyhedron
  82. var pool = Goblin.ObjectPool.pools['GJK2SupportPoint'];
  83.  
  84. for ( var i = 0, faces_length = polyhedron.faces.length; i < faces_length; i++ ) {
  85. // The indexOf checking is required because vertices are shared between faces
  86. if ( pool.indexOf( polyhedron.faces[i].a ) === -1 ) {
  87. Goblin.ObjectPool.freeObject( 'GJK2SupportPoint', polyhedron.faces[i].a );
  88. }
  89. if ( pool.indexOf( polyhedron.faces[i].b ) === -1 ) {
  90. Goblin.ObjectPool.freeObject( 'GJK2SupportPoint', polyhedron.faces[i].b );
  91. }
  92. if ( pool.indexOf( polyhedron.faces[i].c ) === -1 ) {
  93. Goblin.ObjectPool.freeObject( 'GJK2SupportPoint', polyhedron.faces[i].c );
  94. }
  95. }
  96. },
  97.  
  98. /**
  99. * Performs the Expanding Polytope Algorithm a GJK simplex
  100. *
  101. * @method EPA
  102. * @param simplex {Goblin.GjkEpa2.Simplex} Simplex generated by the GJK algorithm
  103. * @return {Goblin.ContactDetails}
  104. */
  105. EPA: (function(){
  106. return function( simplex ) {
  107. // Time to convert the simplex to real faces
  108. // @TODO this should be a priority queue where the position in the queue is ordered by distance from face to origin
  109. var polyhedron = new Goblin.GjkEpa2.Polyhedron( simplex );
  110.  
  111. var i = 0;
  112.  
  113. // Expand the polyhedron until it doesn't expand any more
  114. while ( ++i ) {
  115. polyhedron.findFaceClosestToOrigin();
  116.  
  117. // Find a new support point in the direction of the closest point
  118. if ( polyhedron.closest_face_distance < Goblin.EPSILON ) {
  119. _tmp_vec3_1.copy( polyhedron.faces[polyhedron.closest_face].normal );
  120. } else {
  121. _tmp_vec3_1.copy( polyhedron.closest_point );
  122. }
  123.  
  124. var support_point = Goblin.ObjectPool.getObject( 'GJK2SupportPoint' );
  125. Goblin.GjkEpa2.findSupportPoint( simplex.object_a, simplex.object_b, _tmp_vec3_1, support_point );
  126.  
  127. // Check for terminating condition
  128. _tmp_vec3_1.subtractVectors( support_point.point, polyhedron.closest_point );
  129. var gap = _tmp_vec3_1.lengthSquared();
  130.  
  131. if ( i === Goblin.GjkEpa2.max_iterations || ( gap < Goblin.GjkEpa2.epa_condition && polyhedron.closest_face_distance > Goblin.EPSILON ) ) {
  132.  
  133. // Get a ContactDetails object and fill out its details
  134. var contact = Goblin.ObjectPool.getObject( 'ContactDetails' );
  135. contact.object_a = simplex.object_a;
  136. contact.object_b = simplex.object_b;
  137.  
  138. contact.contact_normal.normalizeVector( polyhedron.closest_point );
  139. if ( contact.contact_normal.lengthSquared() === 0 ) {
  140. contact.contact_normal.subtractVectors( contact.object_b.position, contact.object_a.position );
  141. }
  142. contact.contact_normal.normalize();
  143.  
  144. var barycentric = new Goblin.Vector3();
  145. Goblin.GeometryMethods.findBarycentricCoordinates( polyhedron.closest_point, polyhedron.faces[polyhedron.closest_face].a.point, polyhedron.faces[polyhedron.closest_face].b.point, polyhedron.faces[polyhedron.closest_face].c.point, barycentric );
  146.  
  147. if ( isNaN( barycentric.x ) ) {
  148. // @TODO: Avoid this degenerate case
  149. //console.log( 'Point not in triangle' );
  150. Goblin.GjkEpa2.freePolyhedron( polyhedron );
  151. return null;
  152. }
  153.  
  154. var confirm = {
  155. a: new Goblin.Vector3(),
  156. b: new Goblin.Vector3(),
  157. c: new Goblin.Vector3()
  158. };
  159.  
  160. // Contact coordinates of object a
  161. confirm.a.scaleVector( polyhedron.faces[polyhedron.closest_face].a.witness_a, barycentric.x );
  162. confirm.b.scaleVector( polyhedron.faces[polyhedron.closest_face].b.witness_a, barycentric.y );
  163. confirm.c.scaleVector( polyhedron.faces[polyhedron.closest_face].c.witness_a, barycentric.z );
  164. contact.contact_point_in_a.addVectors( confirm.a, confirm.b );
  165. contact.contact_point_in_a.add( confirm.c );
  166.  
  167. // Contact coordinates of object b
  168. confirm.a.scaleVector( polyhedron.faces[polyhedron.closest_face].a.witness_b, barycentric.x );
  169. confirm.b.scaleVector( polyhedron.faces[polyhedron.closest_face].b.witness_b, barycentric.y );
  170. confirm.c.scaleVector( polyhedron.faces[polyhedron.closest_face].c.witness_b, barycentric.z );
  171. contact.contact_point_in_b.addVectors( confirm.a, confirm.b );
  172. contact.contact_point_in_b.add( confirm.c );
  173.  
  174. // Find actual contact point
  175. contact.contact_point.addVectors( contact.contact_point_in_a, contact.contact_point_in_b );
  176. contact.contact_point.scale( 0.5 );
  177.  
  178. // Set objects' local points
  179. contact.object_a.transform_inverse.transformVector3( contact.contact_point_in_a );
  180. contact.object_b.transform_inverse.transformVector3( contact.contact_point_in_b );
  181.  
  182. // Calculate penetration depth
  183. contact.penetration_depth = polyhedron.closest_point.length();
  184.  
  185. contact.restitution = ( simplex.object_a.restitution + simplex.object_b.restitution ) / 2;
  186. contact.friction = ( simplex.object_a.friction + simplex.object_b.friction ) / 2;
  187.  
  188. Goblin.GjkEpa2.freePolyhedron( polyhedron );
  189.  
  190. return contact;
  191. }
  192.  
  193. polyhedron.addVertex( support_point );
  194. }
  195.  
  196. Goblin.GjkEpa2.freePolyhedron( polyhedron );
  197. return null;
  198. };
  199. })(),
  200.  
  201. Face: function( polyhedron, a, b, c ) {
  202. this.active = true;
  203. //this.polyhedron = polyhedron;
  204. this.a = a;
  205. this.b = b;
  206. this.c = c;
  207. this.normal = new Goblin.Vector3();
  208. this.neighbors = [];
  209.  
  210. _tmp_vec3_1.subtractVectors( b.point, a.point );
  211. _tmp_vec3_2.subtractVectors( c.point, a.point );
  212. this.normal.crossVectors( _tmp_vec3_1, _tmp_vec3_2 );
  213. this.normal.normalize();
  214. }
  215. };
  216.  
  217. Goblin.GjkEpa2.Polyhedron = function( simplex ) {
  218. this.closest_face = null;
  219. this.closest_face_distance = null;
  220. this.closest_point = new Goblin.Vector3();
  221.  
  222. this.faces = [
  223. //BCD, ACB, CAD, DAB
  224. new Goblin.GjkEpa2.Face( this, simplex.points[2], simplex.points[1], simplex.points[0] ),
  225. new Goblin.GjkEpa2.Face( this, simplex.points[3], simplex.points[1], simplex.points[2] ),
  226. new Goblin.GjkEpa2.Face( this, simplex.points[1], simplex.points[3], simplex.points[0] ),
  227. new Goblin.GjkEpa2.Face( this, simplex.points[0], simplex.points[3], simplex.points[2] )
  228. ];
  229.  
  230. this.faces[0].neighbors.push( this.faces[1], this.faces[2], this.faces[3] );
  231. this.faces[1].neighbors.push( this.faces[2], this.faces[0], this.faces[3] );
  232. this.faces[2].neighbors.push( this.faces[1], this.faces[3], this.faces[0] );
  233. this.faces[3].neighbors.push( this.faces[2], this.faces[1], this.faces[0] );
  234. };
  235. Goblin.GjkEpa2.Polyhedron.prototype = {
  236. addVertex: function( vertex )
  237. {
  238. var edges = [], faces = [], i, j, a, b, last_b;
  239. this.faces[this.closest_face].silhouette( vertex, edges );
  240.  
  241. // Re-order the edges if needed
  242. for ( i = 0; i < edges.length - 5; i += 5 ) {
  243. a = edges[i+3];
  244. b = edges[i+4];
  245.  
  246. // Ensure this edge really should be the next one
  247. if ( i !== 0 && last_b !== a ) {
  248. // It shouldn't
  249. for ( j = i + 5; j < edges.length; j += 5 ) {
  250. if ( edges[j+3] === last_b ) {
  251. // Found it
  252. var tmp = edges.slice( i, i + 5 );
  253. edges[i] = edges[j];
  254. edges[i+1] = edges[j+1];
  255. edges[i+2] = edges[j+2];
  256. edges[i+3] = edges[j+3];
  257. edges[i+4] = edges[j+4];
  258. edges[j] = tmp[0];
  259. edges[j+1] = tmp[1];
  260. edges[j+2] = tmp[2];
  261. edges[j+3] = tmp[3];
  262. edges[j+4] = tmp[4];
  263.  
  264. a = edges[i+3];
  265. b = edges[i+4];
  266. break;
  267. }
  268. }
  269. }
  270. last_b = b;
  271. }
  272.  
  273. for ( i = 0; i < edges.length; i += 5 ) {
  274. var neighbor = edges[i];
  275. a = edges[i+3];
  276. b = edges[i+4];
  277.  
  278. var face = new Goblin.GjkEpa2.Face( this, b, vertex, a );
  279. face.neighbors[2] = edges[i];
  280. faces.push( face );
  281.  
  282. neighbor.neighbors[neighbor.neighbors.indexOf( edges[i+2] )] = face;
  283. }
  284.  
  285. for ( i = 0; i < faces.length; i++ ) {
  286. faces[i].neighbors[0] = faces[ i + 1 === faces.length ? 0 : i + 1 ];
  287. faces[i].neighbors[1] = faces[ i - 1 < 0 ? faces.length - 1 : i - 1 ];
  288. }
  289.  
  290. Array.prototype.push.apply( this.faces, faces );
  291.  
  292. return edges;
  293. },
  294.  
  295. findFaceClosestToOrigin: (function(){
  296. var origin = new Goblin.Vector3(),
  297. point = new Goblin.Vector3();
  298.  
  299. return function() {
  300. this.closest_face_distance = Infinity;
  301.  
  302. var distance, i;
  303.  
  304. for ( i = 0; i < this.faces.length; i++ ) {
  305. if ( this.faces[i].active === false ) {
  306. continue;
  307. }
  308.  
  309. Goblin.GeometryMethods.findClosestPointInTriangle( origin, this.faces[i].a.point, this.faces[i].b.point, this.faces[i].c.point, point );
  310. distance = point.lengthSquared();
  311. if ( distance < this.closest_face_distance ) {
  312. this.closest_face_distance = distance;
  313. this.closest_face = i;
  314. this.closest_point.copy( point );
  315. }
  316. }
  317. };
  318. })()
  319. };
  320.  
  321. Goblin.GjkEpa2.Face.prototype = {
  322. /**
  323. * Determines if a vertex is in front of or behind the face
  324. *
  325. * @method classifyVertex
  326. * @param vertex {vec3} Vertex to classify
  327. * @return {Number} If greater than 0 then `vertex' is in front of the face
  328. */
  329. classifyVertex: function( vertex ) {
  330. var w = this.normal.dot( this.a.point );
  331. return this.normal.dot( vertex.point ) - w;
  332. },
  333.  
  334. silhouette: function( point, edges, source ) {
  335. if ( this.active === false ) {
  336. return;
  337. }
  338.  
  339. if ( this.classifyVertex( point ) > 0 ) {
  340. // This face is visible from `point`. Deactivate this face and alert the neighbors
  341. this.active = false;
  342.  
  343. this.neighbors[0].silhouette( point, edges, this );
  344. this.neighbors[1].silhouette( point, edges, this );
  345. this.neighbors[2].silhouette( point, edges, this );
  346. } else if ( source ) {
  347. // This face is a neighbor to a now-silhouetted face, determine which neighbor and replace it
  348. var neighbor_idx = this.neighbors.indexOf( source ),
  349. a, b;
  350. if ( neighbor_idx === 0 ) {
  351. a = this.a;
  352. b = this.b;
  353. } else if ( neighbor_idx === 1 ) {
  354. a = this.b;
  355. b = this.c;
  356. } else {
  357. a = this.c;
  358. b = this.a;
  359. }
  360. edges.push( this, neighbor_idx, source, b, a );
  361. }
  362. }
  363. };
  364.  
  365. (function(){
  366. var ao = new Goblin.Vector3(),
  367. ab = new Goblin.Vector3(),
  368. ac = new Goblin.Vector3(),
  369. ad = new Goblin.Vector3();
  370.  
  371. Goblin.GjkEpa2.Simplex = function( object_a, object_b ) {
  372. this.object_a = object_a;
  373. this.object_b = object_b;
  374. this.points = [];
  375. this.iterations = 0;
  376. this.next_direction = new Goblin.Vector3();
  377. this.updateDirection();
  378. };
  379. Goblin.GjkEpa2.Simplex.prototype = {
  380. addPoint: function() {
  381. if ( ++this.iterations === Goblin.GjkEpa2.max_iterations ) {
  382. return false;
  383. }
  384.  
  385. var support_point = Goblin.ObjectPool.getObject( 'GJK2SupportPoint' );
  386. Goblin.GjkEpa2.findSupportPoint( this.object_a, this.object_b, this.next_direction, support_point );
  387. this.points.push( support_point );
  388.  
  389. if ( this.points[this.points.length-1].point.dot( this.next_direction ) < 0 ) {
  390. // if the last added point was not past the origin in the direction
  391. // then the Minkowski difference cannot contain the origin because
  392. // point added is past the edge of the Minkowski difference
  393. return false;
  394. }
  395.  
  396. if ( this.updateDirection() === true ) {
  397. // Found a collision
  398. return null;
  399. }
  400.  
  401. return support_point;
  402. },
  403.  
  404. findDirectionFromLine: function() {
  405. ao.scaleVector( this.points[1].point, -1 );
  406. ab.subtractVectors( this.points[0].point, this.points[1].point );
  407.  
  408. if ( ab.dot( ao ) < 0 ) {
  409. // Origin is on the opposite side of A from B
  410. this.next_direction.copy( ao );
  411. Goblin.ObjectPool.freeObject( 'GJK2SupportPoint', this.points[1] );
  412. this.points.length = 1; // Remove second point
  413. } else {
  414. // Origin lies between A and B, move on to a 2-simplex
  415. this.next_direction.crossVectors( ab, ao );
  416. this.next_direction.cross( ab );
  417.  
  418. // In the case that `ab` and `ao` are parallel vectors, direction becomes a 0-vector
  419. if (
  420. this.next_direction.x === 0 &&
  421. this.next_direction.y === 0 &&
  422. this.next_direction.z === 0
  423. ) {
  424. ab.normalize();
  425. this.next_direction.x = 1 - Math.abs( ab.x );
  426. this.next_direction.y = 1 - Math.abs( ab.y );
  427. this.next_direction.z = 1 - Math.abs( ab.z );
  428. }
  429. }
  430. },
  431.  
  432. findDirectionFromTriangle: function() {
  433. // Triangle
  434. var a = this.points[2],
  435. b = this.points[1],
  436. c = this.points[0];
  437.  
  438. ao.scaleVector( a.point, -1 ); // ao
  439. ab.subtractVectors( b.point, a.point ); // ab
  440. ac.subtractVectors( c.point, a.point ); // ac
  441.  
  442. // Determine the triangle's normal
  443. _tmp_vec3_1.crossVectors( ab, ac );
  444.  
  445. // Edge cross products
  446. _tmp_vec3_2.crossVectors( ab, _tmp_vec3_1 );
  447. _tmp_vec3_3.crossVectors( _tmp_vec3_1, ac );
  448.  
  449. if ( _tmp_vec3_3.dot( ao ) >= 0 ) {
  450. // Origin lies on side of ac opposite the triangle
  451. if ( ac.dot( ao ) >= 0 ) {
  452. // Origin outside of the ac line, so we form a new
  453. // 1-simplex (line) with points A and C, leaving B behind
  454. this.points.length = 0;
  455. this.points.push( c, a );
  456. Goblin.ObjectPool.freeObject( 'GJK2SupportPoint', b );
  457.  
  458. // New search direction is from ac towards the origin
  459. this.next_direction.crossVectors( ac, ao );
  460. this.next_direction.cross( ac );
  461. } else {
  462. // *
  463. if ( ab.dot( ao ) >= 0 ) {
  464. // Origin outside of the ab line, so we form a new
  465. // 1-simplex (line) with points A and B, leaving C behind
  466. this.points.length = 0;
  467. this.points.push( b, a );
  468. Goblin.ObjectPool.freeObject( 'GJK2SupportPoint', c );
  469.  
  470. // New search direction is from ac towards the origin
  471. this.next_direction.crossVectors( ab, ao );
  472. this.next_direction.cross( ab );
  473. } else {
  474. // only A gives us a good reference point, start over with a 0-simplex
  475. this.points.length = 0;
  476. this.points.push( a );
  477. Goblin.ObjectPool.freeObject( 'GJK2SupportPoint', b );
  478. Goblin.ObjectPool.freeObject( 'GJK2SupportPoint', c );
  479. }
  480. // *
  481. }
  482.  
  483. } else {
  484.  
  485. // Origin lies on the triangle side of ac
  486. if ( _tmp_vec3_2.dot( ao ) >= 0 ) {
  487. // Origin lies on side of ab opposite the triangle
  488.  
  489. // *
  490. if ( ab.dot( ao ) >= 0 ) {
  491. // Origin outside of the ab line, so we form a new
  492. // 1-simplex (line) with points A and B, leaving C behind
  493. this.points.length = 0;
  494. this.points.push( b, a );
  495. Goblin.ObjectPool.freeObject( 'GJK2SupportPoint', c );
  496.  
  497. // New search direction is from ac towards the origin
  498. this.next_direction.crossVectors( ab, ao );
  499. this.next_direction.cross( ab );
  500. } else {
  501. // only A gives us a good reference point, start over with a 0-simplex
  502. this.points.length = 0;
  503. this.points.push( a );
  504. Goblin.ObjectPool.freeObject( 'GJK2SupportPoint', b );
  505. Goblin.ObjectPool.freeObject( 'GJK2SupportPoint', c );
  506. }
  507. // *
  508.  
  509. } else {
  510.  
  511. // Origin lies somewhere in the triangle or above/below it
  512. if ( _tmp_vec3_1.dot( ao ) >= 0 ) {
  513. // Origin is on the front side of the triangle
  514. this.next_direction.copy( _tmp_vec3_1 );
  515. this.points.length = 0;
  516. this.points.push( a, b, c );
  517. } else {
  518. // Origin is on the back side of the triangle
  519. this.next_direction.copy( _tmp_vec3_1 );
  520. this.next_direction.scale( -1 );
  521. }
  522.  
  523. }
  524.  
  525. }
  526. },
  527.  
  528. getFaceNormal: function( a, b, c, destination ) {
  529. ab.subtractVectors( b.point, a.point );
  530. ac.subtractVectors( c.point, a.point );
  531. destination.crossVectors( ab, ac );
  532. destination.normalize();
  533. },
  534.  
  535. faceNormalDotOrigin: function( a, b, c ) {
  536. // Find face normal
  537. this.getFaceNormal( a, b, c, _tmp_vec3_1 );
  538.  
  539. // Find direction of origin from center of face
  540. _tmp_vec3_2.addVectors( a.point, b.point );
  541. _tmp_vec3_2.add( c.point );
  542. _tmp_vec3_2.scale( -3 );
  543. _tmp_vec3_2.normalize();
  544.  
  545. return _tmp_vec3_1.dot( _tmp_vec3_2 );
  546. },
  547.  
  548. findDirectionFromTetrahedron: function() {
  549. var a = this.points[3],
  550. b = this.points[2],
  551. c = this.points[1],
  552. d = this.points[0];
  553.  
  554. // Check each of the four sides to see which one is facing the origin.
  555. // Then keep the three points for that triangle and use its normal as the search direction
  556. // The four faces are BCD, ACB, CAD, DAB
  557. var closest_face = null,
  558. closest_dot = Goblin.EPSILON,
  559. face_dot;
  560.  
  561. // @TODO we end up calculating the "winning" face normal twice, don't do that
  562.  
  563. face_dot = this.faceNormalDotOrigin( b, c, d );
  564. if ( face_dot > closest_dot ) {
  565. closest_face = 1;
  566. closest_dot = face_dot;
  567. }
  568.  
  569. face_dot = this.faceNormalDotOrigin( a, c, b );
  570. if ( face_dot > closest_dot ) {
  571. closest_face = 2;
  572. closest_dot = face_dot;
  573. }
  574.  
  575. face_dot = this.faceNormalDotOrigin( c, a, d );
  576. if ( face_dot > closest_dot ) {
  577. closest_face = 3;
  578. closest_dot = face_dot;
  579. }
  580.  
  581. face_dot = this.faceNormalDotOrigin( d, a, b );
  582. if ( face_dot > closest_dot ) {
  583. closest_face = 4;
  584. closest_dot = face_dot;
  585. }
  586.  
  587. if ( closest_face === null ) {
  588. // We have a collision, ready for EPA
  589. return true;
  590. } else if ( closest_face === 1 ) {
  591. // BCD
  592. this.points.length = 0;
  593. this.points.push( b, c, d );
  594. this.getFaceNormal( b, c, d, _tmp_vec3_1 );
  595. this.next_direction.copy( _tmp_vec3_1 );
  596. } else if ( closest_face === 2 ) {
  597. // ACB
  598. this.points.length = 0;
  599. this.points.push( a, c, b );
  600. this.getFaceNormal( a, c, b, _tmp_vec3_1 );
  601. this.next_direction.copy( _tmp_vec3_1 );
  602. } else if ( closest_face === 3 ) {
  603. // CAD
  604. this.points.length = 0;
  605. this.points.push( c, a, d );
  606. this.getFaceNormal( c, a, d, _tmp_vec3_1 );
  607. this.next_direction.copy( _tmp_vec3_1 );
  608. } else if ( closest_face === 4 ) {
  609. // DAB
  610. this.points.length = 0;
  611. this.points.push( d, a, b );
  612. this.getFaceNormal( d, a, b, _tmp_vec3_1 );
  613. this.next_direction.copy( _tmp_vec3_1 );
  614. }
  615. },
  616.  
  617. containsOrigin: function() {
  618. var a = this.points[3],
  619. b = this.points[2],
  620. c = this.points[1],
  621. d = this.points[0];
  622.  
  623. // Check DCA
  624. ab.subtractVectors( d.point, a.point );
  625. ad.subtractVectors( c.point, a.point );
  626. _tmp_vec3_1.crossVectors( ab, ad );
  627. if ( _tmp_vec3_1.dot( a.point ) > 0 ) {
  628. return false;
  629. }
  630.  
  631. // Check CBA
  632. ab.subtractVectors( c.point, a.point );
  633. ad.subtractVectors( b.point, a.point );
  634. _tmp_vec3_1.crossVectors( ab, ad );
  635. if ( _tmp_vec3_1.dot( a.point ) > 0 ) {
  636. return false;
  637. }
  638.  
  639. // Check ADB
  640. ab.subtractVectors( b.point, a.point );
  641. ad.subtractVectors( d.point, a.point );
  642. _tmp_vec3_1.crossVectors( ab, ad );
  643. if ( _tmp_vec3_1.dot( a.point ) > 0 ) {
  644. return false;
  645. }
  646.  
  647. // Check DCB
  648. ab.subtractVectors( d.point, c.point );
  649. ad.subtractVectors( b.point, c.point );
  650. _tmp_vec3_1.crossVectors( ab, ad );
  651. if ( _tmp_vec3_1.dot( d.point ) > 0 ) {
  652. return false;
  653. }
  654.  
  655. return true;
  656. },
  657.  
  658. updateDirection: function() {
  659. if ( this.points.length === 0 ) {
  660.  
  661. this.next_direction.subtractVectors( this.object_b.position, this.object_a.position );
  662.  
  663. } else if ( this.points.length === 1 ) {
  664.  
  665. this.next_direction.scale( -1 );
  666.  
  667. } else if ( this.points.length === 2 ) {
  668.  
  669. this.findDirectionFromLine();
  670.  
  671. } else if ( this.points.length === 3 ) {
  672.  
  673. this.findDirectionFromTriangle();
  674.  
  675. } else {
  676.  
  677. return this.findDirectionFromTetrahedron();
  678.  
  679. }
  680. }
  681. };
  682. })();
  683.