UNPKG

cannon

Version:

A lightweight 3D physics engine written in JavaScript.

229 lines (198 loc) 6.46 kB
module.exports = GridBroadphase; var Broadphase = require('./Broadphase'); var Vec3 = require('../math/Vec3'); var Shape = require('../shapes/Shape'); /** * Axis aligned uniform grid broadphase. * @class GridBroadphase * @constructor * @extends Broadphase * @todo Needs support for more than just planes and spheres. * @param {Vec3} aabbMin * @param {Vec3} aabbMax * @param {Number} nx Number of boxes along x * @param {Number} ny Number of boxes along y * @param {Number} nz Number of boxes along z */ function GridBroadphase(aabbMin,aabbMax,nx,ny,nz){ Broadphase.apply(this); this.nx = nx || 10; this.ny = ny || 10; this.nz = nz || 10; this.aabbMin = aabbMin || new Vec3(100,100,100); this.aabbMax = aabbMax || new Vec3(-100,-100,-100); var nbins = this.nx * this.ny * this.nz; if (nbins <= 0) { throw "GridBroadphase: Each dimension's n must be >0"; } this.bins = []; this.binLengths = []; //Rather than continually resizing arrays (thrashing the memory), just record length and allow them to grow this.bins.length = nbins; this.binLengths.length = nbins; for (var i=0;i<nbins;i++) { this.bins[i]=[]; this.binLengths[i]=0; } } GridBroadphase.prototype = new Broadphase(); GridBroadphase.prototype.constructor = GridBroadphase; /** * Get all the collision pairs in the physics world * @method collisionPairs * @param {World} world * @param {Array} pairs1 * @param {Array} pairs2 */ var GridBroadphase_collisionPairs_d = new Vec3(); var GridBroadphase_collisionPairs_binPos = new Vec3(); GridBroadphase.prototype.collisionPairs = function(world,pairs1,pairs2){ var N = world.numObjects(), bodies = world.bodies; var max = this.aabbMax, min = this.aabbMin, nx = this.nx, ny = this.ny, nz = this.nz; var xstep = ny*nz; var ystep = nz; var zstep = 1; var xmax = max.x, ymax = max.y, zmax = max.z, xmin = min.x, ymin = min.y, zmin = min.z; var xmult = nx / (xmax-xmin), ymult = ny / (ymax-ymin), zmult = nz / (zmax-zmin); var binsizeX = (xmax - xmin) / nx, binsizeY = (ymax - ymin) / ny, binsizeZ = (zmax - zmin) / nz; var binRadius = Math.sqrt(binsizeX*binsizeX + binsizeY*binsizeY + binsizeZ*binsizeZ) * 0.5; var types = Shape.types; var SPHERE = types.SPHERE, PLANE = types.PLANE, BOX = types.BOX, COMPOUND = types.COMPOUND, CONVEXPOLYHEDRON = types.CONVEXPOLYHEDRON; var bins=this.bins, binLengths=this.binLengths, Nbins=this.bins.length; // Reset bins for(var i=0; i!==Nbins; i++){ binLengths[i] = 0; } var ceil = Math.ceil; var min = Math.min; var max = Math.max; function addBoxToBins(x0,y0,z0,x1,y1,z1,bi) { var xoff0 = ((x0 - xmin) * xmult)|0, yoff0 = ((y0 - ymin) * ymult)|0, zoff0 = ((z0 - zmin) * zmult)|0, xoff1 = ceil((x1 - xmin) * xmult), yoff1 = ceil((y1 - ymin) * ymult), zoff1 = ceil((z1 - zmin) * zmult); if (xoff0 < 0) { xoff0 = 0; } else if (xoff0 >= nx) { xoff0 = nx - 1; } if (yoff0 < 0) { yoff0 = 0; } else if (yoff0 >= ny) { yoff0 = ny - 1; } if (zoff0 < 0) { zoff0 = 0; } else if (zoff0 >= nz) { zoff0 = nz - 1; } if (xoff1 < 0) { xoff1 = 0; } else if (xoff1 >= nx) { xoff1 = nx - 1; } if (yoff1 < 0) { yoff1 = 0; } else if (yoff1 >= ny) { yoff1 = ny - 1; } if (zoff1 < 0) { zoff1 = 0; } else if (zoff1 >= nz) { zoff1 = nz - 1; } xoff0 *= xstep; yoff0 *= ystep; zoff0 *= zstep; xoff1 *= xstep; yoff1 *= ystep; zoff1 *= zstep; for (var xoff = xoff0; xoff <= xoff1; xoff += xstep) { for (var yoff = yoff0; yoff <= yoff1; yoff += ystep) { for (var zoff = zoff0; zoff <= zoff1; zoff += zstep) { var idx = xoff+yoff+zoff; bins[idx][binLengths[idx]++] = bi; } } } } // Put all bodies into the bins for(var i=0; i!==N; i++){ var bi = bodies[i]; var si = bi.shape; switch(si.type){ case SPHERE: // Put in bin // check if overlap with other bins var x = bi.position.x, y = bi.position.y, z = bi.position.z; var r = si.radius; addBoxToBins(x-r, y-r, z-r, x+r, y+r, z+r, bi); break; case PLANE: if(si.worldNormalNeedsUpdate){ si.computeWorldNormal(bi.quaternion); } var planeNormal = si.worldNormal; //Relative position from origin of plane object to the first bin //Incremented as we iterate through the bins var xreset = xmin + binsizeX*0.5 - bi.position.x, yreset = ymin + binsizeY*0.5 - bi.position.y, zreset = zmin + binsizeZ*0.5 - bi.position.z; var d = GridBroadphase_collisionPairs_d; d.set(xreset, yreset, zreset); for (var xi = 0, xoff = 0; xi !== nx; xi++, xoff += xstep, d.y = yreset, d.x += binsizeX) { for (var yi = 0, yoff = 0; yi !== ny; yi++, yoff += ystep, d.z = zreset, d.y += binsizeY) { for (var zi = 0, zoff = 0; zi !== nz; zi++, zoff += zstep, d.z += binsizeZ) { if (d.dot(planeNormal) < binRadius) { var idx = xoff + yoff + zoff; bins[idx][binLengths[idx]++] = bi; } } } } break; default: if (bi.aabbNeedsUpdate) { bi.computeAABB(); } addBoxToBins( bi.aabb.lowerBound.x, bi.aabb.lowerBound.y, bi.aabb.lowerBound.z, bi.aabb.upperBound.x, bi.aabb.upperBound.y, bi.aabb.upperBound.z, bi); break; } } // Check each bin for(var i=0; i!==Nbins; i++){ var binLength = binLengths[i]; //Skip bins with no potential collisions if (binLength > 1) { var bin = bins[i]; // Do N^2 broadphase inside for(var xi=0; xi!==binLength; xi++){ var bi = bin[xi]; for(var yi=0; yi!==xi; yi++){ var bj = bin[yi]; if(this.needBroadphaseCollision(bi,bj)){ this.intersectionTest(bi,bj,pairs1,pairs2); } } } } } // for (var zi = 0, zoff=0; zi < nz; zi++, zoff+= zstep) { // console.log("layer "+zi); // for (var yi = 0, yoff=0; yi < ny; yi++, yoff += ystep) { // var row = ''; // for (var xi = 0, xoff=0; xi < nx; xi++, xoff += xstep) { // var idx = xoff + yoff + zoff; // row += ' ' + binLengths[idx]; // } // console.log(row); // } // } this.makePairsUnique(pairs1,pairs2); };