UNPKG

cannon

Version:

A lightweight 3D physics engine written in JavaScript.

202 lines (176 loc) 5.82 kB
/** * @author schteppe / https://github.com/schteppe */ var VoxelLandscape = function ( world, nx, ny, nz, sx, sy, sz ) { this.nx = nx; this.ny = ny; this.nz = nz; this.sx = sx; this.sy = sy; this.sz = sz; this.world = world; this.map = []; this.boxified = []; this.boxes = []; this.boxShape = new CANNON.Box(new CANNON.Vec3(sx*0.5,sy*0.5,sz*0.5)); var map = this.map, boxes = this.boxes, boxified = this.boxified; // Prepare map for(var i=0; i!==nx; i++){ for(var j=0; j!==ny; j++){ for(var k=0; k!==nz; k++){ map.push(true); boxified.push(false); } } } // User must manually update the map for the first time. }; VoxelLandscape.prototype.getBoxIndex = function(xi,yi,zi){ var nx = this.nx, ny = this.ny, nz = this.nz; if( xi>=0 && xi<nx && yi>=0 && yi<ny && zi>=0 && zi<nz) return xi + nx * yi + nx * ny * zi; else return -1; }; VoxelLandscape.prototype.setFilled = function(xi,yi,zi,filled){ var i = this.getBoxIndex(xi,yi,zi); if(i!==-1) this.map[ i ] = !!filled; }; VoxelLandscape.prototype.isFilled = function(xi,yi,zi){ var i = this.getBoxIndex(xi,yi,zi); if(i!==-1) return this.map[ i ]; else return false; }; VoxelLandscape.prototype.isBoxified = function(xi,yi,zi){ var i = this.getBoxIndex(xi,yi,zi); if(i!==-1) return this.boxified[ i ]; else return false; }; VoxelLandscape.prototype.setBoxified = function(xi,yi,zi,boxified){ return this.boxified[ this.getBoxIndex(xi,yi,zi) ] = !!boxified; }; // Updates "boxes" VoxelLandscape.prototype.update = function(){ var map = this.map, boxes = this.boxes, world = this.world, boxified = this.boxified, nx = this.nx, ny = this.ny, nz = this.nz; // Remove all old boxes for(var i=0; i!==boxes.length; i++){ world.remove(boxes[i]); } boxes.length = 0; // Set whole map to unboxified for(var i=0; i!==boxified.length; i++){ boxified[i] = false; } while(true){ var box; // 1. Get a filled box that we haven't boxified yet for(var i=0; !box && i<nx; i++){ for(var j=0; !box && j<ny; j++){ for(var k=0; !box && k<nz; k++){ if(this.isFilled(i,j,k) && !this.isBoxified(i,j,k)){ box = new CANNON.Body({ mass: 0 }); box.xi = i; // Position box.yi = j; box.zi = k; box.nx = 0; // Size box.ny = 0; box.nz = 0; this.boxes.push(box); } } } } // 2. Check if we can merge it with its neighbors if(box){ // Check what can be merged var xi = box.xi, yi = box.yi, zi = box.zi; box.nx = nx, // merge=1 means merge just with the self box box.ny = ny, box.nz = nz; // Merge in x for(var i=xi; i<nx+1; i++){ if(!this.isFilled(i,yi,zi) || (this.isBoxified(i,yi,zi) && this.getBoxIndex(i,yi,zi)!==-1)){ // Can't merge this box. Make sure we limit the mergeing box.nx = i-xi; break; } } // Merge in y var found = false; for(var i=xi; !found && i<xi+box.nx; i++){ for(var j=yi; !found && j<ny+1; j++){ if(!this.isFilled(i,j,zi) || (this.isBoxified(i,j,zi) && this.getBoxIndex(i,j,zi)!==-1)){ // Can't merge this box. Make sure we limit the mergeing if(box.ny>j-yi) box.ny = j-yi; } } } // Merge in z found = false; for(var i=xi; !found && i<xi+box.nx; i++){ for(var j=yi; !found && j<yi+box.ny; j++){ for(var k=zi; k<nz+1; k++){ if(!this.isFilled(i,j,k) || (this.isBoxified(i,j,k) && this.getBoxIndex(i,j,k)!==-1)){ // Can't merge this box. Make sure we limit the mergeing if(box.nz>k-zi) box.nz = k-zi; } } } } if(box.nx==0) box.nx = 1; if(box.ny==0) box.ny = 1; if(box.nz==0) box.nz = 1; // Set the merged boxes as boxified for(var i=xi; i<xi+box.nx; i++){ for(var j=yi; j<yi+box.ny; j++){ for(var k=zi; k<zi+box.nz; k++){ if( i >= xi && i<=xi+box.nx && j >= yi && j<=yi+box.ny && k >= zi && k<=zi+box.nz){ this.setBoxified(i,j,k,true); } } } } box = false; } else { break; } } // Set box positions var sx = this.sx, sy = this.sy, sz = this.sz; for(var i=0; i<this.boxes.length; i++){ var b = this.boxes[i]; b.position.set( b.xi * sx + b.nx*sx*0.5, b.yi * sy + b.ny*sy*0.5, b.zi * sz + b.nz*sz*0.5 ); // Replace box shapes b.addShape(new CANNON.Box(new CANNON.Vec3(b.nx*sx*0.5, b.ny*sy*0.5, b.nz*sz*0.5))); //b.aabbNeedsUpdate = true; world.add(b); //this.boxes.push(box); } };