@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
222 lines (174 loc) • 6.8 kB
JavaScript
// see https://github.com/matthias-research/pages/blob/d62033fec407a6e0dec408f85c371c231612a3b6/tenMinutePhysics/17-fluidSim.html#L92
export class Fluid {
constructor(density, numX, numY, h) {
this.density = density;
this.numX = numX + 2;
this.numY = numY + 2;
this.numCells = this.numX * this.numY;
this.h = h;
this.u = new Float32Array(this.numCells);
this.v = new Float32Array(this.numCells);
this.newU = new Float32Array(this.numCells);
this.newV = new Float32Array(this.numCells);
this.p = new Float32Array(this.numCells);
this.s = new Float32Array(this.numCells);
this.m = new Float32Array(this.numCells);
this.newM = new Float32Array(this.numCells);
this.m.fill(1.0)
}
integrate(dt, gravity) {
var n = this.numY;
for (var i = 1; i < this.numX; i++) {
for (var j = 1; j < this.numY-1; j++) {
if (this.s[i*n + j] != 0.0 && this.s[i*n + j-1] != 0.0)
this.v[i*n + j] += gravity * dt;
}
}
}
solveIncompressibility(numIters, dt) {
var n = this.numY;
var cp = this.density * this.h / dt;
for (var iter = 0; iter < numIters; iter++) {
for (var i = 1; i < this.numX-1; i++) {
for (var j = 1; j < this.numY-1; j++) {
if (this.s[i*n + j] == 0.0)
continue;
var s = this.s[i*n + j];
var sx0 = this.s[(i-1)*n + j];
var sx1 = this.s[(i+1)*n + j];
var sy0 = this.s[i*n + j-1];
var sy1 = this.s[i*n + j+1];
var s = sx0 + sx1 + sy0 + sy1;
if (s == 0.0)
continue;
var div = this.u[(i+1)*n + j] - this.u[i*n + j] +
this.v[i*n + j+1] - this.v[i*n + j];
var p = -div / s;
p *= scene.overRelaxation;
this.p[i*n + j] += cp * p;
this.u[i*n + j] -= sx0 * p;
this.u[(i+1)*n + j] += sx1 * p;
this.v[i*n + j] -= sy0 * p;
this.v[i*n + j+1] += sy1 * p;
}
}
}
}
extrapolate() {
var n = this.numY;
for (var i = 0; i < this.numX; i++) {
this.u[i*n + 0] = this.u[i*n + 1];
this.u[i*n + this.numY-1] = this.u[i*n + this.numY-2];
}
for (var j = 0; j < this.numY; j++) {
this.v[0*n + j] = this.v[1*n + j];
this.v[(this.numX-1)*n + j] = this.v[(this.numX-2)*n + j]
}
}
sampleField(x, y, field) {
var n = this.numY;
var h = this.h;
var h1 = 1.0 / h;
var h2 = 0.5 * h;
x = Math.max(Math.min(x, this.numX * h), h);
y = Math.max(Math.min(y, this.numY * h), h);
var dx = 0.0;
var dy = 0.0;
var f;
switch (field) {
case U_FIELD: f = this.u; dy = h2; break;
case V_FIELD: f = this.v; dx = h2; break;
case S_FIELD: f = this.m; dx = h2; dy = h2; break
}
var x0 = Math.min(Math.floor((x-dx)*h1), this.numX-1);
var tx = ((x-dx) - x0*h) * h1;
var x1 = Math.min(x0 + 1, this.numX-1);
var y0 = Math.min(Math.floor((y-dy)*h1), this.numY-1);
var ty = ((y-dy) - y0*h) * h1;
var y1 = Math.min(y0 + 1, this.numY-1);
var sx = 1.0 - tx;
var sy = 1.0 - ty;
var val = sx*sy * f[x0*n + y0] +
tx*sy * f[x1*n + y0] +
tx*ty * f[x1*n + y1] +
sx*ty * f[x0*n + y1];
return val;
}
avgU(i, j) {
var n = this.numY;
var u = (this.u[i*n + j-1] + this.u[i*n + j] +
this.u[(i+1)*n + j-1] + this.u[(i+1)*n + j]) * 0.25;
return u;
}
avgV(i, j) {
var n = this.numY;
var v = (this.v[(i-1)*n + j] + this.v[i*n + j] +
this.v[(i-1)*n + j+1] + this.v[i*n + j+1]) * 0.25;
return v;
}
advectVel(dt) {
this.newU.set(this.u);
this.newV.set(this.v);
var n = this.numY;
var h = this.h;
var h2 = 0.5 * h;
for (var i = 1; i < this.numX; i++) {
for (var j = 1; j < this.numY; j++) {
cnt++;
// u component
if (this.s[i*n + j] != 0.0 && this.s[(i-1)*n + j] != 0.0 && j < this.numY - 1) {
var x = i*h;
var y = j*h + h2;
var u = this.u[i*n + j];
var v = this.avgV(i, j);
// var v = this.sampleField(x,y, V_FIELD);
x = x - dt*u;
y = y - dt*v;
u = this.sampleField(x,y, U_FIELD);
this.newU[i*n + j] = u;
}
// v component
if (this.s[i*n + j] != 0.0 && this.s[i*n + j-1] != 0.0 && i < this.numX - 1) {
var x = i*h + h2;
var y = j*h;
var u = this.avgU(i, j);
// var u = this.sampleField(x,y, U_FIELD);
var v = this.v[i*n + j];
x = x - dt*u;
y = y - dt*v;
v = this.sampleField(x,y, V_FIELD);
this.newV[i*n + j] = v;
}
}
}
this.u.set(this.newU);
this.v.set(this.newV);
}
advectSmoke(dt) {
this.newM.set(this.m);
var n = this.numY;
var h = this.h;
var h2 = 0.5 * h;
for (var i = 1; i < this.numX-1; i++) {
for (var j = 1; j < this.numY-1; j++) {
if (this.s[i*n + j] != 0.0) {
var u = (this.u[i*n + j] + this.u[(i+1)*n + j]) * 0.5;
var v = (this.v[i*n + j] + this.v[i*n + j+1]) * 0.5;
var x = i*h + h2 - dt*u;
var y = j*h + h2 - dt*v;
this.newM[i*n + j] = this.sampleField(x,y, S_FIELD);
}
}
}
this.m.set(this.newM);
}
// ----------------- end of simulator ------------------------------
simulate(dt, gravity, numIters) {
this.integrate(dt, gravity);
this.p.fill(0.0);
this.solveIncompressibility(numIters, dt);
this.extrapolate();
this.advectVel(dt);
this.advectSmoke(dt);
}
}