smokePuff function
A puff of smoke that billows out and thins over its life — the engine's own, so a six-way particle has something to show with no asset at all.
A ball whose edge is broken up by a few octaves of value noise, growing
from half the cube to most of it while its density falls. seed picks the
noise; the same seed bakes the same bytes, in the browser as well.
Implementation
SixWayField smokePuff({int seed = 1}) {
double hash(int x, int y, int z) {
// An integer hash, bit-mixed; any fixed permutation would do, and this one
// needs no table. **In 32 bits, with every product under 2^53**: it was
// written for the VM's 64-bit integers, and compiled for the web the same
// products lost their low bits as doubles, so a browser baked another
// puff and `smoke-six-way` stood 2% apart from the native backends.
final h0 = _add32(
_add32(_mul32(x, 374761393), _mul32(y, 668265263)),
_add32(_mul32(z, 2147483647), _mul32(seed, 144269)),
);
final h1 = _mul32(h0 ^ (h0 >> 13), 1274126177);
final h = h1 ^ (h1 >> 16);
return (h & 0xffff) / 0xffff;
}
double smooth(double f) => f * f * (3.0 - 2.0 * f);
double noise(double x, double y, double z) {
final ix = x.floor();
final iy = y.floor();
final iz = z.floor();
final fx = smooth(x - ix);
final fy = smooth(y - iy);
final fz = smooth(z - iz);
double lerp(double a, double b, double f) => a + (b - a) * f;
double corner(int dx, int dy, int dz) => hash(ix + dx, iy + dy, iz + dz);
return lerp(
lerp(
lerp(corner(0, 0, 0), corner(1, 0, 0), fx),
lerp(corner(0, 1, 0), corner(1, 1, 0), fx),
fy,
),
lerp(
lerp(corner(0, 0, 1), corner(1, 0, 1), fx),
lerp(corner(0, 1, 1), corner(1, 1, 1), fx),
fy,
),
fz,
);
}
return (x, y, z, t) {
final radius = 0.45 + 0.4 * t;
final r = math.sqrt(x * x + y * y + z * z) / radius;
if (r >= 1.4) return 0.0;
// Rising: the noise drifts down through the ball, so the billows move up.
final drift = t * 1.5;
final n =
noise(x * 3.0, y * 3.0 - drift, z * 3.0) * 0.6 +
noise(x * 6.0, y * 6.0 - drift * 2.0, z * 6.0) * 0.3 +
noise(x * 12.0, y * 12.0, z * 12.0) * 0.1;
final shape = 1.0 - r + (n - 0.5) * 0.9;
return (shape * 2.5).clamp(0.0, 1.0) * (1.0 - 0.6 * t);
};
}