cannon
Version:
A lightweight 3D physics engine written in JavaScript.
202 lines (176 loc) • 5.82 kB
JavaScript
/**
* @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);
}
};