API Docs for: 0.6.1
Show:

File: src/shapes/ConvexPolyhedron.js

  1. module.exports = ConvexPolyhedron;
  2.  
  3. var Shape = require('./Shape');
  4. var Vec3 = require('../math/Vec3');
  5. var Quaternion = require('../math/Quaternion');
  6. var Transform = require('../math/Transform');
  7.  
  8. /**
  9. * A set of polygons describing a convex shape.
  10. * @class ConvexPolyhedron
  11. * @constructor
  12. * @extends Shape
  13. * @description The shape MUST be convex for the code to work properly. No polygons may be coplanar (contained
  14. * in the same 3D plane), instead these should be merged into one polygon.
  15. *
  16. * @param {array} points An array of Vec3's
  17. * @param {array} faces Array of integer arrays, describing which vertices that is included in each face.
  18. *
  19. * @author qiao / https://github.com/qiao (original author, see https://github.com/qiao/three.js/commit/85026f0c769e4000148a67d45a9e9b9c5108836f)
  20. * @author schteppe / https://github.com/schteppe
  21. * @see http://www.altdevblogaday.com/2011/05/13/contact-generation-between-3d-convex-meshes/
  22. * @see http://bullet.googlecode.com/svn/trunk/src/BulletCollision/NarrowPhaseCollision/btPolyhedralContactClipping.cpp
  23. *
  24. * @todo Move the clipping functions to ContactGenerator?
  25. * @todo Automatically merge coplanar polygons in constructor.
  26. */
  27. function ConvexPolyhedron(points, faces, uniqueAxes) {
  28. var that = this;
  29. Shape.call(this);
  30. this.type = Shape.types.CONVEXPOLYHEDRON;
  31.  
  32. /**
  33. * Array of Vec3
  34. * @property vertices
  35. * @type {Array}
  36. */
  37. this.vertices = points||[];
  38.  
  39. this.worldVertices = []; // World transformed version of .vertices
  40. this.worldVerticesNeedsUpdate = true;
  41.  
  42. /**
  43. * Array of integer arrays, indicating which vertices each face consists of
  44. * @property faces
  45. * @type {Array}
  46. */
  47. this.faces = faces||[];
  48.  
  49. /**
  50. * Array of Vec3
  51. * @property faceNormals
  52. * @type {Array}
  53. */
  54. this.faceNormals = [];
  55. this.computeNormals();
  56.  
  57. this.worldFaceNormalsNeedsUpdate = true;
  58. this.worldFaceNormals = []; // World transformed version of .faceNormals
  59.  
  60. /**
  61. * Array of Vec3
  62. * @property uniqueEdges
  63. * @type {Array}
  64. */
  65. this.uniqueEdges = [];
  66.  
  67. /**
  68. * If given, these locally defined, normalized axes are the only ones being checked when doing separating axis check.
  69. * @property {Array} uniqueAxes
  70. */
  71. this.uniqueAxes = uniqueAxes ? uniqueAxes.slice() : null;
  72.  
  73. this.computeEdges();
  74. this.updateBoundingSphereRadius();
  75. }
  76. ConvexPolyhedron.prototype = new Shape();
  77. ConvexPolyhedron.prototype.constructor = ConvexPolyhedron;
  78.  
  79. var computeEdges_tmpEdge = new Vec3();
  80. /**
  81. * Computes uniqueEdges
  82. * @method computeEdges
  83. */
  84. ConvexPolyhedron.prototype.computeEdges = function(){
  85. var faces = this.faces;
  86. var vertices = this.vertices;
  87. var nv = vertices.length;
  88. var edges = this.uniqueEdges;
  89.  
  90. edges.length = 0;
  91.  
  92. var edge = computeEdges_tmpEdge;
  93.  
  94. for(var i=0; i !== faces.length; i++){
  95. var face = faces[i];
  96. var numVertices = face.length;
  97. for(var j = 0; j !== numVertices; j++){
  98. var k = ( j+1 ) % numVertices;
  99. vertices[face[j]].vsub(vertices[face[k]], edge);
  100. edge.normalize();
  101. var found = false;
  102. for(var p=0; p !== edges.length; p++){
  103. if (edges[p].almostEquals(edge) || edges[p].almostEquals(edge)){
  104. found = true;
  105. break;
  106. }
  107. }
  108.  
  109. if (!found){
  110. edges.push(edge.clone());
  111. }
  112. }
  113. }
  114. };
  115.  
  116. /**
  117. * Compute the normals of the faces. Will reuse existing Vec3 objects in the .faceNormals array if they exist.
  118. * @method computeNormals
  119. */
  120. ConvexPolyhedron.prototype.computeNormals = function(){
  121. this.faceNormals.length = this.faces.length;
  122.  
  123. // Generate normals
  124. for(var i=0; i<this.faces.length; i++){
  125.  
  126. // Check so all vertices exists for this face
  127. for(var j=0; j<this.faces[i].length; j++){
  128. if(!this.vertices[this.faces[i][j]]){
  129. throw new Error("Vertex "+this.faces[i][j]+" not found!");
  130. }
  131. }
  132.  
  133. var n = this.faceNormals[i] || new Vec3();
  134. this.getFaceNormal(i,n);
  135. n.negate(n);
  136. this.faceNormals[i] = n;
  137. var vertex = this.vertices[this.faces[i][0]];
  138. if(n.dot(vertex) < 0){
  139. console.error(".faceNormals[" + i + "] = Vec3("+n.toString()+") looks like it points into the shape? The vertices follow. Make sure they are ordered CCW around the normal, using the right hand rule.");
  140. for(var j=0; j<this.faces[i].length; j++){
  141. console.warn(".vertices["+this.faces[i][j]+"] = Vec3("+this.vertices[this.faces[i][j]].toString()+")");
  142. }
  143. }
  144. }
  145. };
  146.  
  147. /**
  148. * Get face normal given 3 vertices
  149. * @static
  150. * @method getFaceNormal
  151. * @param {Vec3} va
  152. * @param {Vec3} vb
  153. * @param {Vec3} vc
  154. * @param {Vec3} target
  155. */
  156. var cb = new Vec3();
  157. var ab = new Vec3();
  158. ConvexPolyhedron.computeNormal = function ( va, vb, vc, target ) {
  159. vb.vsub(va,ab);
  160. vc.vsub(vb,cb);
  161. cb.cross(ab,target);
  162. if ( !target.isZero() ) {
  163. target.normalize();
  164. }
  165. };
  166.  
  167. /**
  168. * Compute the normal of a face from its vertices
  169. * @method getFaceNormal
  170. * @param {Number} i
  171. * @param {Vec3} target
  172. */
  173. ConvexPolyhedron.prototype.getFaceNormal = function(i,target){
  174. var f = this.faces[i];
  175. var va = this.vertices[f[0]];
  176. var vb = this.vertices[f[1]];
  177. var vc = this.vertices[f[2]];
  178. return ConvexPolyhedron.computeNormal(va,vb,vc,target);
  179. };
  180.  
  181. /**
  182. * @method clipAgainstHull
  183. * @param {Vec3} posA
  184. * @param {Quaternion} quatA
  185. * @param {ConvexPolyhedron} hullB
  186. * @param {Vec3} posB
  187. * @param {Quaternion} quatB
  188. * @param {Vec3} separatingNormal
  189. * @param {Number} minDist Clamp distance
  190. * @param {Number} maxDist
  191. * @param {array} result The an array of contact point objects, see clipFaceAgainstHull
  192. * @see http://bullet.googlecode.com/svn/trunk/src/BulletCollision/NarrowPhaseCollision/btPolyhedralContactClipping.cpp
  193. */
  194. var cah_WorldNormal = new Vec3();
  195. ConvexPolyhedron.prototype.clipAgainstHull = function(posA,quatA,hullB,posB,quatB,separatingNormal,minDist,maxDist,result){
  196. var WorldNormal = cah_WorldNormal;
  197. var hullA = this;
  198. var curMaxDist = maxDist;
  199. var closestFaceB = -1;
  200. var dmax = -Number.MAX_VALUE;
  201. for(var face=0; face < hullB.faces.length; face++){
  202. WorldNormal.copy(hullB.faceNormals[face]);
  203. quatB.vmult(WorldNormal,WorldNormal);
  204. //posB.vadd(WorldNormal,WorldNormal);
  205. var d = WorldNormal.dot(separatingNormal);
  206. if (d > dmax){
  207. dmax = d;
  208. closestFaceB = face;
  209. }
  210. }
  211. var worldVertsB1 = [];
  212. var polyB = hullB.faces[closestFaceB];
  213. var numVertices = polyB.length;
  214. for(var e0=0; e0<numVertices; e0++){
  215. var b = hullB.vertices[polyB[e0]];
  216. var worldb = new Vec3();
  217. worldb.copy(b);
  218. quatB.vmult(worldb,worldb);
  219. posB.vadd(worldb,worldb);
  220. worldVertsB1.push(worldb);
  221. }
  222.  
  223. if (closestFaceB>=0){
  224. this.clipFaceAgainstHull(separatingNormal,
  225. posA,
  226. quatA,
  227. worldVertsB1,
  228. minDist,
  229. maxDist,
  230. result);
  231. }
  232. };
  233.  
  234. /**
  235. * Find the separating axis between this hull and another
  236. * @method findSeparatingAxis
  237. * @param {ConvexPolyhedron} hullB
  238. * @param {Vec3} posA
  239. * @param {Quaternion} quatA
  240. * @param {Vec3} posB
  241. * @param {Quaternion} quatB
  242. * @param {Vec3} target The target vector to save the axis in
  243. * @return {bool} Returns false if a separation is found, else true
  244. */
  245. var fsa_faceANormalWS3 = new Vec3(),
  246. fsa_Worldnormal1 = new Vec3(),
  247. fsa_deltaC = new Vec3(),
  248. fsa_worldEdge0 = new Vec3(),
  249. fsa_worldEdge1 = new Vec3(),
  250. fsa_Cross = new Vec3();
  251. ConvexPolyhedron.prototype.findSeparatingAxis = function(hullB,posA,quatA,posB,quatB,target, faceListA, faceListB){
  252. var faceANormalWS3 = fsa_faceANormalWS3,
  253. Worldnormal1 = fsa_Worldnormal1,
  254. deltaC = fsa_deltaC,
  255. worldEdge0 = fsa_worldEdge0,
  256. worldEdge1 = fsa_worldEdge1,
  257. Cross = fsa_Cross;
  258.  
  259. var dmin = Number.MAX_VALUE;
  260. var hullA = this;
  261. var curPlaneTests=0;
  262.  
  263. if(!hullA.uniqueAxes){
  264.  
  265. var numFacesA = faceListA ? faceListA.length : hullA.faces.length;
  266.  
  267. // Test face normals from hullA
  268. for(var i=0; i<numFacesA; i++){
  269. var fi = faceListA ? faceListA[i] : i;
  270.  
  271. // Get world face normal
  272. faceANormalWS3.copy(hullA.faceNormals[fi]);
  273. quatA.vmult(faceANormalWS3,faceANormalWS3);
  274.  
  275. var d = hullA.testSepAxis(faceANormalWS3, hullB, posA, quatA, posB, quatB);
  276. if(d===false){
  277. return false;
  278. }
  279.  
  280. if(d<dmin){
  281. dmin = d;
  282. target.copy(faceANormalWS3);
  283. }
  284. }
  285.  
  286. } else {
  287.  
  288. // Test unique axes
  289. for(var i = 0; i !== hullA.uniqueAxes.length; i++){
  290.  
  291. // Get world axis
  292. quatA.vmult(hullA.uniqueAxes[i],faceANormalWS3);
  293.  
  294. var d = hullA.testSepAxis(faceANormalWS3, hullB, posA, quatA, posB, quatB);
  295. if(d===false){
  296. return false;
  297. }
  298.  
  299. if(d<dmin){
  300. dmin = d;
  301. target.copy(faceANormalWS3);
  302. }
  303. }
  304. }
  305.  
  306. if(!hullB.uniqueAxes){
  307.  
  308. // Test face normals from hullB
  309. var numFacesB = faceListB ? faceListB.length : hullB.faces.length;
  310. for(var i=0;i<numFacesB;i++){
  311.  
  312. var fi = faceListB ? faceListB[i] : i;
  313.  
  314. Worldnormal1.copy(hullB.faceNormals[fi]);
  315. quatB.vmult(Worldnormal1,Worldnormal1);
  316. curPlaneTests++;
  317. var d = hullA.testSepAxis(Worldnormal1, hullB,posA,quatA,posB,quatB);
  318. if(d===false){
  319. return false;
  320. }
  321.  
  322. if(d<dmin){
  323. dmin = d;
  324. target.copy(Worldnormal1);
  325. }
  326. }
  327. } else {
  328.  
  329. // Test unique axes in B
  330. for(var i = 0; i !== hullB.uniqueAxes.length; i++){
  331. quatB.vmult(hullB.uniqueAxes[i],Worldnormal1);
  332.  
  333. curPlaneTests++;
  334. var d = hullA.testSepAxis(Worldnormal1, hullB,posA,quatA,posB,quatB);
  335. if(d===false){
  336. return false;
  337. }
  338.  
  339. if(d<dmin){
  340. dmin = d;
  341. target.copy(Worldnormal1);
  342. }
  343. }
  344. }
  345.  
  346. // Test edges
  347. for(var e0=0; e0 !== hullA.uniqueEdges.length; e0++){
  348.  
  349. // Get world edge
  350. quatA.vmult(hullA.uniqueEdges[e0],worldEdge0);
  351.  
  352. for(var e1=0; e1 !== hullB.uniqueEdges.length; e1++){
  353.  
  354. // Get world edge 2
  355. quatB.vmult(hullB.uniqueEdges[e1], worldEdge1);
  356. worldEdge0.cross(worldEdge1,Cross);
  357.  
  358. if(!Cross.almostZero()){
  359. Cross.normalize();
  360. var dist = hullA.testSepAxis(Cross, hullB, posA, quatA, posB, quatB);
  361. if(dist === false){
  362. return false;
  363. }
  364. if(dist < dmin){
  365. dmin = dist;
  366. target.copy(Cross);
  367. }
  368. }
  369. }
  370. }
  371.  
  372. posB.vsub(posA,deltaC);
  373. if((deltaC.dot(target))>0.0){
  374. target.negate(target);
  375. }
  376.  
  377. return true;
  378. };
  379.  
  380. var maxminA=[], maxminB=[];
  381.  
  382. /**
  383. * Test separating axis against two hulls. Both hulls are projected onto the axis and the overlap size is returned if there is one.
  384. * @method testSepAxis
  385. * @param {Vec3} axis
  386. * @param {ConvexPolyhedron} hullB
  387. * @param {Vec3} posA
  388. * @param {Quaternion} quatA
  389. * @param {Vec3} posB
  390. * @param {Quaternion} quatB
  391. * @return {number} The overlap depth, or FALSE if no penetration.
  392. */
  393. ConvexPolyhedron.prototype.testSepAxis = function(axis, hullB, posA, quatA, posB, quatB){
  394. var hullA=this;
  395. ConvexPolyhedron.project(hullA, axis, posA, quatA, maxminA);
  396. ConvexPolyhedron.project(hullB, axis, posB, quatB, maxminB);
  397. var maxA = maxminA[0];
  398. var minA = maxminA[1];
  399. var maxB = maxminB[0];
  400. var minB = maxminB[1];
  401. if(maxA<minB || maxB<minA){
  402. return false; // Separated
  403. }
  404. var d0 = maxA - minB;
  405. var d1 = maxB - minA;
  406. var depth = d0<d1 ? d0:d1;
  407. return depth;
  408. };
  409.  
  410. var cli_aabbmin = new Vec3(),
  411. cli_aabbmax = new Vec3();
  412.  
  413. /**
  414. * @method calculateLocalInertia
  415. * @param {Number} mass
  416. * @param {Vec3} target
  417. */
  418. ConvexPolyhedron.prototype.calculateLocalInertia = function(mass,target){
  419. // Approximate with box inertia
  420. // Exact inertia calculation is overkill, but see http://geometrictools.com/Documentation/PolyhedralMassProperties.pdf for the correct way to do it
  421. this.computeLocalAABB(cli_aabbmin,cli_aabbmax);
  422. var x = cli_aabbmax.x - cli_aabbmin.x,
  423. y = cli_aabbmax.y - cli_aabbmin.y,
  424. z = cli_aabbmax.z - cli_aabbmin.z;
  425. target.x = 1.0 / 12.0 * mass * ( 2*y*2*y + 2*z*2*z );
  426. target.y = 1.0 / 12.0 * mass * ( 2*x*2*x + 2*z*2*z );
  427. target.z = 1.0 / 12.0 * mass * ( 2*y*2*y + 2*x*2*x );
  428. };
  429.  
  430. /**
  431. * @method getPlaneConstantOfFace
  432. * @param {Number} face_i Index of the face
  433. * @return {Number}
  434. */
  435. ConvexPolyhedron.prototype.getPlaneConstantOfFace = function(face_i){
  436. var f = this.faces[face_i];
  437. var n = this.faceNormals[face_i];
  438. var v = this.vertices[f[0]];
  439. var c = -n.dot(v);
  440. return c;
  441. };
  442.  
  443. /**
  444. * Clip a face against a hull.
  445. * @method clipFaceAgainstHull
  446. * @param {Vec3} separatingNormal
  447. * @param {Vec3} posA
  448. * @param {Quaternion} quatA
  449. * @param {Array} worldVertsB1 An array of Vec3 with vertices in the world frame.
  450. * @param {Number} minDist Distance clamping
  451. * @param {Number} maxDist
  452. * @param Array result Array to store resulting contact points in. Will be objects with properties: point, depth, normal. These are represented in world coordinates.
  453. */
  454. var cfah_faceANormalWS = new Vec3(),
  455. cfah_edge0 = new Vec3(),
  456. cfah_WorldEdge0 = new Vec3(),
  457. cfah_worldPlaneAnormal1 = new Vec3(),
  458. cfah_planeNormalWS1 = new Vec3(),
  459. cfah_worldA1 = new Vec3(),
  460. cfah_localPlaneNormal = new Vec3(),
  461. cfah_planeNormalWS = new Vec3();
  462. ConvexPolyhedron.prototype.clipFaceAgainstHull = function(separatingNormal, posA, quatA, worldVertsB1, minDist, maxDist,result){
  463. var faceANormalWS = cfah_faceANormalWS,
  464. edge0 = cfah_edge0,
  465. WorldEdge0 = cfah_WorldEdge0,
  466. worldPlaneAnormal1 = cfah_worldPlaneAnormal1,
  467. planeNormalWS1 = cfah_planeNormalWS1,
  468. worldA1 = cfah_worldA1,
  469. localPlaneNormal = cfah_localPlaneNormal,
  470. planeNormalWS = cfah_planeNormalWS;
  471.  
  472. var hullA = this;
  473. var worldVertsB2 = [];
  474. var pVtxIn = worldVertsB1;
  475. var pVtxOut = worldVertsB2;
  476. // Find the face with normal closest to the separating axis
  477. var closestFaceA = -1;
  478. var dmin = Number.MAX_VALUE;
  479. for(var face=0; face<hullA.faces.length; face++){
  480. faceANormalWS.copy(hullA.faceNormals[face]);
  481. quatA.vmult(faceANormalWS,faceANormalWS);
  482. //posA.vadd(faceANormalWS,faceANormalWS);
  483. var d = faceANormalWS.dot(separatingNormal);
  484. if (d < dmin){
  485. dmin = d;
  486. closestFaceA = face;
  487. }
  488. }
  489. if (closestFaceA < 0){
  490. // console.log("--- did not find any closest face... ---");
  491. return;
  492. }
  493. //console.log("closest A: ",closestFaceA);
  494. // Get the face and construct connected faces
  495. var polyA = hullA.faces[closestFaceA];
  496. polyA.connectedFaces = [];
  497. for(var i=0; i<hullA.faces.length; i++){
  498. for(var j=0; j<hullA.faces[i].length; j++){
  499. if(polyA.indexOf(hullA.faces[i][j])!==-1 /* Sharing a vertex*/ && i!==closestFaceA /* Not the one we are looking for connections from */ && polyA.connectedFaces.indexOf(i)===-1 /* Not already added */ ){
  500. polyA.connectedFaces.push(i);
  501. }
  502. }
  503. }
  504. // Clip the polygon to the back of the planes of all faces of hull A, that are adjacent to the witness face
  505. var numContacts = pVtxIn.length;
  506. var numVerticesA = polyA.length;
  507. var res = [];
  508. for(var e0=0; e0<numVerticesA; e0++){
  509. var a = hullA.vertices[polyA[e0]];
  510. var b = hullA.vertices[polyA[(e0+1)%numVerticesA]];
  511. a.vsub(b,edge0);
  512. WorldEdge0.copy(edge0);
  513. quatA.vmult(WorldEdge0,WorldEdge0);
  514. posA.vadd(WorldEdge0,WorldEdge0);
  515. worldPlaneAnormal1.copy(this.faceNormals[closestFaceA]);//transA.getBasis()* btVector3(polyA.m_plane[0],polyA.m_plane[1],polyA.m_plane[2]);
  516. quatA.vmult(worldPlaneAnormal1,worldPlaneAnormal1);
  517. posA.vadd(worldPlaneAnormal1,worldPlaneAnormal1);
  518. WorldEdge0.cross(worldPlaneAnormal1,planeNormalWS1);
  519. planeNormalWS1.negate(planeNormalWS1);
  520. worldA1.copy(a);
  521. quatA.vmult(worldA1,worldA1);
  522. posA.vadd(worldA1,worldA1);
  523. var planeEqWS1 = -worldA1.dot(planeNormalWS1);
  524. var planeEqWS;
  525. if(true){
  526. var otherFace = polyA.connectedFaces[e0];
  527. localPlaneNormal.copy(this.faceNormals[otherFace]);
  528. var localPlaneEq = this.getPlaneConstantOfFace(otherFace);
  529.  
  530. planeNormalWS.copy(localPlaneNormal);
  531. quatA.vmult(planeNormalWS,planeNormalWS);
  532. //posA.vadd(planeNormalWS,planeNormalWS);
  533. var planeEqWS = localPlaneEq - planeNormalWS.dot(posA);
  534. } else {
  535. planeNormalWS.copy(planeNormalWS1);
  536. planeEqWS = planeEqWS1;
  537. }
  538.  
  539. // Clip face against our constructed plane
  540. this.clipFaceAgainstPlane(pVtxIn, pVtxOut, planeNormalWS, planeEqWS);
  541.  
  542. // Throw away all clipped points, but save the reamining until next clip
  543. while(pVtxIn.length){
  544. pVtxIn.shift();
  545. }
  546. while(pVtxOut.length){
  547. pVtxIn.push(pVtxOut.shift());
  548. }
  549. }
  550.  
  551. //console.log("Resulting points after clip:",pVtxIn);
  552.  
  553. // only keep contact points that are behind the witness face
  554. localPlaneNormal.copy(this.faceNormals[closestFaceA]);
  555.  
  556. var localPlaneEq = this.getPlaneConstantOfFace(closestFaceA);
  557. planeNormalWS.copy(localPlaneNormal);
  558. quatA.vmult(planeNormalWS,planeNormalWS);
  559.  
  560. var planeEqWS = localPlaneEq - planeNormalWS.dot(posA);
  561. for (var i=0; i<pVtxIn.length; i++){
  562. var depth = planeNormalWS.dot(pVtxIn[i]) + planeEqWS; //???
  563. /*console.log("depth calc from normal=",planeNormalWS.toString()," and constant "+planeEqWS+" and vertex ",pVtxIn[i].toString()," gives "+depth);*/
  564. if (depth <=minDist){
  565. console.log("clamped: depth="+depth+" to minDist="+(minDist+""));
  566. depth = minDist;
  567. }
  568.  
  569. if (depth <=maxDist){
  570. var point = pVtxIn[i];
  571. if(depth<=0){
  572. /*console.log("Got contact point ",point.toString(),
  573. ", depth=",depth,
  574. "contact normal=",separatingNormal.toString(),
  575. "plane",planeNormalWS.toString(),
  576. "planeConstant",planeEqWS);*/
  577. var p = {
  578. point:point,
  579. normal:planeNormalWS,
  580. depth: depth,
  581. };
  582. result.push(p);
  583. }
  584. }
  585. }
  586. };
  587.  
  588. /**
  589. * Clip a face in a hull against the back of a plane.
  590. * @method clipFaceAgainstPlane
  591. * @param {Array} inVertices
  592. * @param {Array} outVertices
  593. * @param {Vec3} planeNormal
  594. * @param {Number} planeConstant The constant in the mathematical plane equation
  595. */
  596. ConvexPolyhedron.prototype.clipFaceAgainstPlane = function(inVertices,outVertices, planeNormal, planeConstant){
  597. var n_dot_first, n_dot_last;
  598. var numVerts = inVertices.length;
  599.  
  600. if(numVerts < 2){
  601. return outVertices;
  602. }
  603.  
  604. var firstVertex = inVertices[inVertices.length-1],
  605. lastVertex = inVertices[0];
  606.  
  607. n_dot_first = planeNormal.dot(firstVertex) + planeConstant;
  608.  
  609. for(var vi = 0; vi < numVerts; vi++){
  610. lastVertex = inVertices[vi];
  611. n_dot_last = planeNormal.dot(lastVertex) + planeConstant;
  612. if(n_dot_first < 0){
  613. if(n_dot_last < 0){
  614. // Start < 0, end < 0, so output lastVertex
  615. var newv = new Vec3();
  616. newv.copy(lastVertex);
  617. outVertices.push(newv);
  618. } else {
  619. // Start < 0, end >= 0, so output intersection
  620. var newv = new Vec3();
  621. firstVertex.lerp(lastVertex,
  622. n_dot_first / (n_dot_first - n_dot_last),
  623. newv);
  624. outVertices.push(newv);
  625. }
  626. } else {
  627. if(n_dot_last<0){
  628. // Start >= 0, end < 0 so output intersection and end
  629. var newv = new Vec3();
  630. firstVertex.lerp(lastVertex,
  631. n_dot_first / (n_dot_first - n_dot_last),
  632. newv);
  633. outVertices.push(newv);
  634. outVertices.push(lastVertex);
  635. }
  636. }
  637. firstVertex = lastVertex;
  638. n_dot_first = n_dot_last;
  639. }
  640. return outVertices;
  641. };
  642.  
  643. // Updates .worldVertices and sets .worldVerticesNeedsUpdate to false.
  644. ConvexPolyhedron.prototype.computeWorldVertices = function(position,quat){
  645. var N = this.vertices.length;
  646. while(this.worldVertices.length < N){
  647. this.worldVertices.push( new Vec3() );
  648. }
  649.  
  650. var verts = this.vertices,
  651. worldVerts = this.worldVertices;
  652. for(var i=0; i!==N; i++){
  653. quat.vmult( verts[i] , worldVerts[i] );
  654. position.vadd( worldVerts[i] , worldVerts[i] );
  655. }
  656.  
  657. this.worldVerticesNeedsUpdate = false;
  658. };
  659.  
  660. var computeLocalAABB_worldVert = new Vec3();
  661. ConvexPolyhedron.prototype.computeLocalAABB = function(aabbmin,aabbmax){
  662. var n = this.vertices.length,
  663. vertices = this.vertices,
  664. worldVert = computeLocalAABB_worldVert;
  665.  
  666. aabbmin.set(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  667. aabbmax.set(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
  668.  
  669. for(var i=0; i<n; i++){
  670. var v = vertices[i];
  671. if (v.x < aabbmin.x){
  672. aabbmin.x = v.x;
  673. } else if(v.x > aabbmax.x){
  674. aabbmax.x = v.x;
  675. }
  676. if (v.y < aabbmin.y){
  677. aabbmin.y = v.y;
  678. } else if(v.y > aabbmax.y){
  679. aabbmax.y = v.y;
  680. }
  681. if (v.z < aabbmin.z){
  682. aabbmin.z = v.z;
  683. } else if(v.z > aabbmax.z){
  684. aabbmax.z = v.z;
  685. }
  686. }
  687. };
  688.  
  689. /**
  690. * Updates .worldVertices and sets .worldVerticesNeedsUpdate to false.
  691. * @method computeWorldFaceNormals
  692. * @param {Quaternion} quat
  693. */
  694. ConvexPolyhedron.prototype.computeWorldFaceNormals = function(quat){
  695. var N = this.faceNormals.length;
  696. while(this.worldFaceNormals.length < N){
  697. this.worldFaceNormals.push( new Vec3() );
  698. }
  699.  
  700. var normals = this.faceNormals,
  701. worldNormals = this.worldFaceNormals;
  702. for(var i=0; i!==N; i++){
  703. quat.vmult( normals[i] , worldNormals[i] );
  704. }
  705.  
  706. this.worldFaceNormalsNeedsUpdate = false;
  707. };
  708.  
  709. /**
  710. * @method updateBoundingSphereRadius
  711. */
  712. ConvexPolyhedron.prototype.updateBoundingSphereRadius = function(){
  713. // Assume points are distributed with local (0,0,0) as center
  714. var max2 = 0;
  715. var verts = this.vertices;
  716. for(var i=0, N=verts.length; i!==N; i++) {
  717. var norm2 = verts[i].norm2();
  718. if(norm2 > max2){
  719. max2 = norm2;
  720. }
  721. }
  722. this.boundingSphereRadius = Math.sqrt(max2);
  723. };
  724.  
  725. var tempWorldVertex = new Vec3();
  726.  
  727. /**
  728. * @method calculateWorldAABB
  729. * @param {Vec3} pos
  730. * @param {Quaternion} quat
  731. * @param {Vec3} min
  732. * @param {Vec3} max
  733. */
  734. ConvexPolyhedron.prototype.calculateWorldAABB = function(pos,quat,min,max){
  735. var n = this.vertices.length, verts = this.vertices;
  736. var minx,miny,minz,maxx,maxy,maxz;
  737. for(var i=0; i<n; i++){
  738. tempWorldVertex.copy(verts[i]);
  739. quat.vmult(tempWorldVertex,tempWorldVertex);
  740. pos.vadd(tempWorldVertex,tempWorldVertex);
  741. var v = tempWorldVertex;
  742. if (v.x < minx || minx===undefined){
  743. minx = v.x;
  744. } else if(v.x > maxx || maxx===undefined){
  745. maxx = v.x;
  746. }
  747.  
  748. if (v.y < miny || miny===undefined){
  749. miny = v.y;
  750. } else if(v.y > maxy || maxy===undefined){
  751. maxy = v.y;
  752. }
  753.  
  754. if (v.z < minz || minz===undefined){
  755. minz = v.z;
  756. } else if(v.z > maxz || maxz===undefined){
  757. maxz = v.z;
  758. }
  759. }
  760. min.set(minx,miny,minz);
  761. max.set(maxx,maxy,maxz);
  762. };
  763.  
  764. /**
  765. * Get approximate convex volume
  766. * @method volume
  767. * @return {Number}
  768. */
  769. ConvexPolyhedron.prototype.volume = function(){
  770. return 4.0 * Math.PI * this.boundingSphereRadius / 3.0;
  771. };
  772.  
  773. /**
  774. * Get an average of all the vertices positions
  775. * @method getAveragePointLocal
  776. * @param {Vec3} target
  777. * @return {Vec3}
  778. */
  779. ConvexPolyhedron.prototype.getAveragePointLocal = function(target){
  780. target = target || new Vec3();
  781. var n = this.vertices.length,
  782. verts = this.vertices;
  783. for(var i=0; i<n; i++){
  784. target.vadd(verts[i],target);
  785. }
  786. target.mult(1/n,target);
  787. return target;
  788. };
  789.  
  790. /**
  791. * Transform all local points. Will change the .vertices
  792. * @method transformAllPoints
  793. * @param {Vec3} offset
  794. * @param {Quaternion} quat
  795. */
  796. ConvexPolyhedron.prototype.transformAllPoints = function(offset,quat){
  797. var n = this.vertices.length,
  798. verts = this.vertices;
  799.  
  800. // Apply rotation
  801. if(quat){
  802. // Rotate vertices
  803. for(var i=0; i<n; i++){
  804. var v = verts[i];
  805. quat.vmult(v,v);
  806. }
  807. // Rotate face normals
  808. for(var i=0; i<this.faceNormals.length; i++){
  809. var v = this.faceNormals[i];
  810. quat.vmult(v,v);
  811. }
  812. /*
  813. // Rotate edges
  814. for(var i=0; i<this.uniqueEdges.length; i++){
  815. var v = this.uniqueEdges[i];
  816. quat.vmult(v,v);
  817. }*/
  818. }
  819.  
  820. // Apply offset
  821. if(offset){
  822. for(var i=0; i<n; i++){
  823. var v = verts[i];
  824. v.vadd(offset,v);
  825. }
  826. }
  827. };
  828.  
  829. /**
  830. * Checks whether p is inside the polyhedra. Must be in local coords. The point lies outside of the convex hull of the other points if and only if the direction of all the vectors from it to those other points are on less than one half of a sphere around it.
  831. * @method pointIsInside
  832. * @param {Vec3} p A point given in local coordinates
  833. * @return {Boolean}
  834. */
  835. var ConvexPolyhedron_pointIsInside = new Vec3();
  836. var ConvexPolyhedron_vToP = new Vec3();
  837. var ConvexPolyhedron_vToPointInside = new Vec3();
  838. ConvexPolyhedron.prototype.pointIsInside = function(p){
  839. var n = this.vertices.length,
  840. verts = this.vertices,
  841. faces = this.faces,
  842. normals = this.faceNormals;
  843. var positiveResult = null;
  844. var N = this.faces.length;
  845. var pointInside = ConvexPolyhedron_pointIsInside;
  846. this.getAveragePointLocal(pointInside);
  847. for(var i=0; i<N; i++){
  848. var numVertices = this.faces[i].length;
  849. var n = normals[i];
  850. var v = verts[faces[i][0]]; // We only need one point in the face
  851.  
  852. // This dot product determines which side of the edge the point is
  853. var vToP = ConvexPolyhedron_vToP;
  854. p.vsub(v,vToP);
  855. var r1 = n.dot(vToP);
  856.  
  857. var vToPointInside = ConvexPolyhedron_vToPointInside;
  858. pointInside.vsub(v,vToPointInside);
  859. var r2 = n.dot(vToPointInside);
  860.  
  861. if((r1<0 && r2>0) || (r1>0 && r2<0)){
  862. return false; // Encountered some other sign. Exit.
  863. } else {
  864. }
  865. }
  866.  
  867. // If we got here, all dot products were of the same sign.
  868. return positiveResult ? 1 : -1;
  869. };
  870.  
  871. /**
  872. * Get max and min dot product of a convex hull at position (pos,quat) projected onto an axis. Results are saved in the array maxmin.
  873. * @static
  874. * @method project
  875. * @param {ConvexPolyhedron} hull
  876. * @param {Vec3} axis
  877. * @param {Vec3} pos
  878. * @param {Quaternion} quat
  879. * @param {array} result result[0] and result[1] will be set to maximum and minimum, respectively.
  880. */
  881. var project_worldVertex = new Vec3();
  882. var project_localAxis = new Vec3();
  883. var project_localOrigin = new Vec3();
  884. ConvexPolyhedron.project = function(hull, axis, pos, quat, result){
  885. var n = hull.vertices.length,
  886. worldVertex = project_worldVertex,
  887. localAxis = project_localAxis,
  888. max = 0,
  889. min = 0,
  890. localOrigin = project_localOrigin,
  891. vs = hull.vertices;
  892.  
  893. localOrigin.setZero();
  894.  
  895. // Transform the axis to local
  896. Transform.vectorToLocalFrame(pos, quat, axis, localAxis);
  897. Transform.pointToLocalFrame(pos, quat, localOrigin, localOrigin);
  898. var add = localOrigin.dot(localAxis);
  899.  
  900. min = max = vs[0].dot(localAxis);
  901.  
  902. for(var i = 1; i < n; i++){
  903. var val = vs[i].dot(localAxis);
  904.  
  905. if(val > max){
  906. max = val;
  907. }
  908.  
  909. if(val < min){
  910. min = val;
  911. }
  912. }
  913.  
  914. min -= add;
  915. max -= add;
  916.  
  917. if(min > max){
  918. // Inconsistent - swap
  919. var temp = min;
  920. min = max;
  921. max = temp;
  922. }
  923. // Output
  924. result[0] = max;
  925. result[1] = min;
  926. };
  927.