mirror of
https://github.com/wassname/phaser.git
synced 2026-07-14 01:10:16 +08:00
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:
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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});
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user