Started revamp of the Tilemap system. Also removed old 'Advanced Physics' and dropped in p2.js which is what I hope we'll eventually use.

This commit is contained in:
photonstorm
2013-10-11 04:42:11 +01:00
parent a7230aa769
commit b868c2cb1b
72 changed files with 6704 additions and 6454 deletions
+190
View File
@@ -0,0 +1,190 @@
var vec2 = require('../math/vec2'),
Solver = require('./Solver');
module.exports = GSSolver;
var ARRAY_TYPE = Float32Array || Array;
/**
* Iterative Gauss-Seidel constraint equation solver.
*
* @class GSSolver
* @constructor
* @extends Solver
* @param {Object} [options]
* @param {Number} options.iterations
* @param {Number} options.timeStep
* @param {Number} options.stiffness
* @param {Number} options.relaxation
* @param {Number} options.tolerance
*/
function GSSolver(options){
Solver.call(this);
options = options || {};
this.iterations = options.iterations || 10;
this.tolerance = options.tolerance || 0;
this.debug = options.debug || false;
this.arrayStep = 30;
this.lambda = new ARRAY_TYPE(this.arrayStep);
this.Bs = new ARRAY_TYPE(this.arrayStep);
this.invCs = new ARRAY_TYPE(this.arrayStep);
/**
* Whether to use .stiffness and .relaxation parameters from the Solver instead of each Equation individually.
* @type {Boolean}
* @property useGlobalEquationParameters
*/
this.useGlobalEquationParameters = true;
/**
* Global equation stiffness.
* @property stiffness
* @type {Number}
*/
this.stiffness = 1e6;
/**
* Global equation relaxation.
* @property relaxation
* @type {Number}
*/
this.relaxation = 4;
/**
* Set to true to set all right hand side terms to zero when solving. Can be handy for a few applications.
* @property useZeroRHS
* @type {Boolean}
*/
this.useZeroRHS = false;
};
GSSolver.prototype = new Solver();
/**
* Set stiffness parameters
*
* @method setSpookParams
* @param {number} k
* @param {number} d
* @deprecated
*/
GSSolver.prototype.setSpookParams = function(k,d){
this.stiffness = k;
this.relaxation = d;
};
/**
* Solve the system of equations
* @method solve
* @param {Number} dt Time step
* @param {World} world World to solve
*/
GSSolver.prototype.solve = function(dt,world){
var iter = 0,
maxIter = this.iterations,
tolSquared = this.tolerance*this.tolerance,
equations = this.equations,
Neq = equations.length,
bodies = world.bodies,
Nbodies = world.bodies.length,
h = dt,
d = this.relaxation,
k = this.stiffness,
eps = 4.0 / (h * h * k * (1 + 4 * d)),
a = 4.0 / (h * (1 + 4 * d)),
b = (4.0 * d) / (1 + 4 * d),
useGlobalParams = this.useGlobalEquationParameters,
add = vec2.add,
set = vec2.set,
useZeroRHS = this.useZeroRHS;
// Things that does not change during iteration can be computed once
if(this.lambda.length < Neq){
this.lambda = new ARRAY_TYPE(Neq + this.arrayStep);
this.Bs = new ARRAY_TYPE(Neq + this.arrayStep);
this.invCs = new ARRAY_TYPE(Neq + this.arrayStep);
}
var invCs = this.invCs,
Bs = this.Bs,
lambda = this.lambda;
for(var i=0; i!==Neq; i++){
var c = equations[i];
lambda[i] = 0.0;
var _a = a,
_b = b,
_eps = eps;
if(!useGlobalParams){
if(h !== c.h) c.updateSpookParams(h);
_a = c.a;
_b = c.b;
_eps = c.eps;
}
Bs[i] = c.computeB(_a,_b,h);
invCs[i] = 1.0 / c.computeC(_eps);
}
var q, B, c, invC, deltalambda, deltalambdaTot, GWlambda, lambdaj;
if(Neq !== 0){
var i,j, minForce, maxForce, lambdaj_plus_deltalambda;
// Reset vlambda
for(i=0; i!==Nbodies; i++){
var b=bodies[i], vlambda=b.vlambda;
set(vlambda,0,0);
b.wlambda = 0;
}
// Iterate over equations
for(iter=0; iter!==maxIter; iter++){
// Accumulate the total error for each iteration.
deltalambdaTot = 0.0;
for(j=0; j!==Neq; j++){
c = equations[j];
var _eps = useGlobalParams ? eps : c.eps;
// Compute iteration
maxForce = c.maxForce;
minForce = c.minForce;
B = Bs[j];
invC = invCs[j];
lambdaj = lambda[j];
GWlambda = c.computeGWlambda(_eps);
if(useZeroRHS) B = 0;
deltalambda = invC * ( B - GWlambda - _eps * lambdaj );
// Clamp if we are not within the min/max interval
lambdaj_plus_deltalambda = lambdaj + deltalambda;
if(lambdaj_plus_deltalambda < minForce){
deltalambda = minForce - lambdaj;
} else if(lambdaj_plus_deltalambda > maxForce){
deltalambda = maxForce - lambdaj;
}
lambda[j] += deltalambda;
deltalambdaTot += Math.abs(deltalambda);
c.addToWlambda(deltalambda);
}
// If the total error is small enough - stop iterate
if(deltalambdaTot*deltalambdaTot <= tolSquared) break;
}
// Add result to velocity
for(i=0; i!==Nbodies; i++){
var b=bodies[i], v=b.velocity;
add( v, v, b.vlambda);
b.angularVelocity += b.wlambda;
}
}
errorTot = deltalambdaTot;
};
+81
View File
@@ -0,0 +1,81 @@
module.exports = Island;
/**
* An island of bodies connected with equations.
* @class Island
* @constructor
*/
function Island(){
/**
* Current equations in this island.
* @property equations
* @type {Array}
*/
this.equations = [];
/**
* Current bodies in this island.
* @property bodies
* @type {Array}
*/
this.bodies = [];
}
/**
* Clean this island from bodies and equations.
* @method reset
*/
Island.prototype.reset = function(){
this.equations.length = this.bodies.length = 0;
}
/**
* Get all unique bodies in this island.
* @method getBodies
* @return {Array} An array of Body
*/
Island.prototype.getBodies = function(){
var bodies = [],
bodyIds = [],
eqs = this.equations;
for(var i=0; i!==eqs.length; i++){
var eq = eqs[i];
if(bodyIds.indexOf(eq.bi.id)===-1){
bodies.push(eq.bi);
bodyIds.push(eq.bi.id);
}
if(bodyIds.indexOf(eq.bj.id)===-1){
bodies.push(eq.bj);
bodyIds.push(eq.bj.id);
}
}
return bodies;
};
/**
* Solves all constraints in the group of islands.
* @method solve
* @param {Number} dt
* @param {Solver} solver
*/
Island.prototype.solve = function(dt,solver){
var bodies = [];
solver.removeAllEquations();
// Add equations to solver
var numEquations = this.equations.length;
for(var j=0; j!==numEquations; j++){
solver.addEquation(this.equations[j]);
}
var islandBodies = this.getBodies();
var numBodies = islandBodies.length;
for(var j=0; j!==numBodies; j++){
bodies.push(islandBodies[j]);
}
// Solve
solver.solve(dt,{bodies:bodies});
};
+159
View File
@@ -0,0 +1,159 @@
var Solver = require('./Solver')
, vec2 = require('../math/vec2')
, Island = require('../solver/Island')
, Body = require('../objects/Body')
, STATIC = Body.STATIC
module.exports = IslandSolver;
/**
* Splits the system of bodies and equations into independent islands
*
* @class IslandSolver
* @constructor
* @param {Solver} subsolver
* @extends Solver
*/
function IslandSolver(subsolver){
Solver.call(this);
var that = this;
/**
* The solver used in the workers.
* @property subsolver
* @type {Solver}
*/
this.subsolver = subsolver;
/**
* Number of islands
* @property numIslands
* @type {number}
*/
this.numIslands = 0;
// Pooling of node objects saves some GC load
this._nodePool = [];
};
IslandSolver.prototype = new Object(Solver.prototype);
function getUnvisitedNode(nodes){
var Nnodes = nodes.length;
for(var i=0; i!==Nnodes; i++){
var node = nodes[i];
if(!node.visited && !(node.body.motionState & STATIC)){ // correct?
return node;
}
}
return false;
}
function bfs(root,visitFunc){
var queue = [];
queue.push(root);
root.visited = true;
visitFunc(root);
while(queue.length) {
var node = queue.pop();
// Loop over unvisited child nodes
var child;
while((child = getUnvisitedNode(node.children))) {
child.visited = true;
visitFunc(child);
queue.push(child);
}
}
}
/**
* Solves the full system.
* @method solve
* @param {Number} dt
* @param {World} world
*/
IslandSolver.prototype.solve = function(dt,world){
var nodes = [],
bodies=world.bodies,
equations=this.equations,
Neq=equations.length,
Nbodies=bodies.length,
subsolver=this.subsolver,
workers = this._workers,
workerData = this._workerData,
workerIslandGroups = this._workerIslandGroups;
// Create needed nodes, reuse if possible
for(var i=0; i!==Nbodies; i++){
if(this._nodePool.length)
nodes.push( this._nodePool.pop() );
else {
nodes.push({
body:bodies[i],
children:[],
eqs:[],
visited:false
});
}
}
// Reset node values
for(var i=0; i!==Nbodies; i++){
var node = nodes[i];
node.body = bodies[i];
node.children.length = 0;
node.eqs.length = 0;
node.visited = false;
}
// Add connectivity data. Each equation connects 2 bodies.
for(var k=0; k!==Neq; k++){
var eq=equations[k],
i=bodies.indexOf(eq.bi),
j=bodies.indexOf(eq.bj),
ni=nodes[i],
nj=nodes[j];
ni.children.push(nj);
ni.eqs.push(eq);
nj.children.push(ni);
nj.eqs.push(eq);
}
// The BFS search algorithm needs a traversal function. What we do is gather all bodies and equations connected.
var child, n=0, eqs=[], bds=[];
function visitFunc(node){
bds.push(node.body);
var Neqs = node.eqs.length;
for(var i=0; i!==Neqs; i++){
var eq = node.eqs[i];
if(eqs.indexOf(eq) === -1){
eqs.push(eq);
}
}
}
// Get islands
var islands = [];
while((child = getUnvisitedNode(nodes))){
var island = new Island(); // @todo Should be reused from somewhere
eqs.length = 0;
bds.length = 0;
bfs(child,visitFunc); // run search algo to gather an island of bodies
// Add equations to island
var Neqs = eqs.length;
for(var i=0; i!==Neqs; i++){
var eq = eqs[i];
island.equations.push(eq);
}
n++;
islands.push(island);
}
this.numIslands = n;
// Solve islands
for(var i=0; i<islands.length; i++){
islands[i].solve(dt,this.subsolver);
}
};
+65
View File
@@ -0,0 +1,65 @@
var Utils = require('../utils/Utils');
module.exports = Solver;
/**
* Base class for constraint solvers.
* @class Solver
* @constructor
*/
function Solver(){
/**
* Current equations in the solver.
*
* @property equations
* @type {Array}
*/
this.equations = [];
};
Solver.prototype.solve = function(dt,world){
throw new Error("Solver.solve should be implemented by subclasses!");
};
/**
* Add an equation to be solved.
*
* @method addEquation
* @param {Equation} eq
*/
Solver.prototype.addEquation = function(eq){
this.equations.push(eq);
};
/**
* Add equations. Same as .addEquation, but this time the argument is an array of Equations
*
* @method addEquations
* @param {Array} eqs
*/
Solver.prototype.addEquations = function(eqs){
Utils.appendArray(this.equations,eqs);
};
/**
* Remove an equation.
*
* @method removeEquation
* @param {Equation} eq
*/
Solver.prototype.removeEquation = function(eq){
var i = this.equations.indexOf(eq);
if(i!=-1)
this.equations.splice(i,1);
};
/**
* Remove all currently added equations.
*
* @method removeAllEquations
*/
Solver.prototype.removeAllEquations = function(){
this.equations.length=0;
};