phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
774 lines (673 loc) • 22.8 kB
JavaScript
/**
* Hash a number or list of numbers to a simplex noise value.
*
* A hash is an unpredictable transformation of an input,
* which always returns the same output from the same input.
* It is useful for generating random data in a predictable way.
*
* The output is in the range -1 to 1.
*
* You can set the config object to control the output.
* You can add octaves of detail, add a turbulent warp,
* adjust gradient flow, and set a randomness seed.
*
* Simplex noise is an evolution of Perlin noise, both invented by Ken Perlin.
* It is a form of gradient noise, which forms a smooth, continuous field:
* very similar inputs produce very similar outputs, although the field
* itself is still random.
*
* Simplex noise works by assigning random gradients to points on a grid,
* splitting space into grid cells (using a minimal triangular shape called a simplex),
* identifying which grid cell the input belongs to,
* and blending between the gradients of that simplex.
*
* This version uses a flow parameter to animate the noise field.
* The flow value rotates gradients in place, creating a periodic
* shifting pattern.
*
* This implementation deliberately copies the shaders used in the
* `NoiseSimplex2D` and `NoiseSimplex3D` game object shaders.
* They behave in similar fashion, and the config objects are very similar.
* HashSimplex does not support value factor/add/power terms,
* as you can simply process the output yourself.
*
* HashSimplex also supports 1-dimensional input.
* This is automatically padded to 2 dimensions.
*
* Simplex performance varies depending on octaves and warp settings.
* You might compute a thousand or so hashes per millisecond,
* depending on device and workload.
*
* Note: If you specify dimensional properties in the config parameter,
* ensure they are at least as long as the input vector.
* Missing values can corrupt the result.
*
* Disclaimer: This function is intended for efficiently generating visual
* variety. It is not intended for cryptographic use. Do not use it for
* security/authentication/encryption purposes. Use a proper tool instead.
*
* @example
* // Create a smoothly shifting field of points.
* // Use noiseSeed to get different outputs from the same inputs.
* for (let x = 0; x < 800; x += 8)
* {
* for (let y = 0; y < 600; y += 8)
* {
* this.add.rectangle(
* Phaser.Math.HashSimplex([ x, y ], { noiseSeed: [ 1, 2 ] }) * 8,
* Phaser.Math.HashSimplex([ x, y ], { noiseSeed: [ 3, 4 ] }) * 8,
* 2,
* 2,
* 0xffffff
* );
* }
* }
*
* @function Phaser.Math.HashSimplex
* @since 4.0.0
*
* @param {number | number[]} vector - The input vector to hash. 1 to 3 numbers.
* @param {Phaser.Types.Math.HashSimplexConfig} [config] - The configuration of the noise field.
*
* @return {number} A noise value in the range -1 to 1.
*/
var HashSimplex = function (vector, config)
{
if (typeof vector === 'number') { vector = [ vector ]; }
if (!config) { config = {}; }
var axes = Math.min(3, vector.length);
var value = 0;
// Support 1D input.
while (axes < 2)
{
vector.push(0);
axes++;
}
// Match the config names used by the shaders and typedefs.
var iterations = config.noiseIterations || 1;
var warpIterations = config.noiseWarpIterations || 1;
var detailPower = config.noiseDetailPower || 2;
var flowPower = config.noiseFlowPower || 2;
var contributionPower = config.noiseContributionPower || 2;
var warpDetailPower = config.noiseWarpDetailPower || 2;
var warpFlowPower = config.noiseWarpFlowPower || 2;
var warpContributionPower = config.noiseWarpContributionPower || 2;
var cells = config.noiseCells || [ 32, 32, 32 ];
var offset = config.noiseOffset || [ 0, 0, 0 ];
var warpAmount = config.noiseWarpAmount || 0;
if (config.noiseSeed)
{
var defaultSeed = [ 1, 2, 3 ];
for (var i = 0; i < axes; i++)
{
seed[i] = config.noiseSeed[i] === undefined ? defaultSeed[i] : config.noiseSeed[i];
}
}
for (i = 0; i < axes; i++)
{
coord[i] = vector[i] * cells[i] + offset[i];
}
// Optional warping pass, mirroring the shader behaviour.
if (warpAmount > 0 && warpIterations > 0 && axes >= 2)
{
if (axes === 2)
{
// 2D warp uses two offset coordinates.
var warpCoord1 = iterate(warpIterations, 2, coord, config, warpDetailPower, warpFlowPower, warpContributionPower);
itCoord[0] = coord[0] + o2[0];
itCoord[1] = coord[1] + o2[1];
var warpCoord2 = iterate(warpIterations, 2, itCoord, config, warpDetailPower, warpFlowPower, warpContributionPower);
coord[0] += warpCoord1 * warpAmount;
coord[1] += warpCoord2 * warpAmount;
}
else if (axes === 3)
{
// 3D warp uses three offset coordinates.
var warp3DCoord1 = iterate(warpIterations, 3, coord, config, warpDetailPower, warpFlowPower, warpContributionPower);
itCoord[0] = coord[0] + o2[0];
itCoord[1] = coord[1] + o2[1];
itCoord[2] = coord[2] + o2[2];
var warp3DCoord2 = iterate(warpIterations, 3, itCoord, config, warpDetailPower, warpFlowPower, warpContributionPower);
itCoord[0] = coord[0] + o3[0];
itCoord[1] = coord[1] + o3[1];
itCoord[2] = coord[2] + o3[2];
var warp3DCoord3 = iterate(warpIterations, 3, itCoord, config, warpDetailPower, warpFlowPower, warpContributionPower);
coord[0] += warp3DCoord1 * warpAmount;
coord[1] += warp3DCoord2 * warpAmount;
coord[2] += warp3DCoord3 * warpAmount;
}
}
value = iterate(iterations, axes, coord, config, detailPower, flowPower, contributionPower);
return value;
};
var coord = [ 0, 0, 0 ];
var itCoord = [ 0, 0, 0 ];
var itPeriod = [ 0, 0, 0 ];
var o2 = [ 11.3, 23.7, 13.1 ];
var o3 = [ 29.9, 2.3, 31.7 ];
var seed = [ 0, 0, 0 ];
var iterate = function (iterations, axes, coord, config, detailPower, flowPower, contributionPower)
{
var i;
var value = 0;
var itValue = 0;
var baseCells = config.noiseCells || [ 32, 32, 32 ];
var period = config.noisePeriod || baseCells;
var flow = config.noiseFlow || 0;
for (var iteration = 0; iteration < iterations; iteration++)
{
var detailScale = Math.pow(detailPower, iteration);
var flowScale = Math.pow(flowPower, iteration);
for (i = 0; i < axes; i++)
{
itCoord[i] = (coord[i] + iteration) * detailScale;
}
for (i = 0; i < axes; i++)
{
itPeriod[i] = period[i] * detailScale;
}
if (axes === 2)
{
itValue = psrdnoise2d(itCoord, itPeriod, flow * flowScale + iteration);
}
else if (axes === 3)
{
itValue = psrdnoise3d(itCoord, itPeriod, flow * flowScale + iteration);
}
value += itValue / Math.pow(contributionPower, iteration);
}
return value;
};
// The noise functions are derived from
// https://github.com/stegu/psrdnoise/,
// specifically psrdnoise2.glsl and
// psrdnoise3.glsl, version 2021-12-02
// Copyright (c) 2021 Stefan Gustavson and Ian McEwan
// (stefan.gustavson@liu.se, ijm567@gmail.com)
// Published under the MIT license, see:
// https://opensource.org/licenses/MIT
// Temporary module-scope containers for composite data types
var _psrd2d = {
uv: { x: 0, y: 0 },
i0: { x: 0, y: 0 },
f0: { x: 0, y: 0 },
o1: { x: 0, y: 0 },
i1: { x: 0, y: 0 }, i2: { x: 0, y: 0 },
v0: { x: 0, y: 0 }, v1: { x: 0, y: 0 }, v2: { x: 0, y: 0 },
x0: { x: 0, y: 0 }, x1: { x: 0, y: 0 }, x2: { x: 0, y: 0 },
// For vec3's
iu: [ 0, 0, 0 ], iv: [ 0, 0, 0 ], xw: [ 0, 0, 0 ], yw: [ 0, 0, 0 ],
hash: [ 0, 0, 0 ],
psi: [ 0, 0, 0 ], gx: [ 0, 0, 0 ], gy: [ 0, 0, 0 ],
g0: { x: 0, y: 0 }, g1: { x: 0, y: 0 }, g2: { x: 0, y: 0 },
w: [ 0, 0, 0 ], w2: [ 0, 0, 0 ], w4: [ 0, 0, 0 ],
gdotx: [ 0, 0, 0 ]
};
function dot2 (a, b) { return a.x * b.x + a.y * b.y; }
var psrdnoise2d = function (x, period, alpha)
{
var i;
var uv = _psrd2d.uv;
var i0 = _psrd2d.i0;
var f0 = _psrd2d.f0;
var o1 = _psrd2d.o1;
var i1 = _psrd2d.i1;
var i2 = _psrd2d.i2;
var v0 = _psrd2d.v0;
var v1 = _psrd2d.v1;
var v2 = _psrd2d.v2;
var x0 = _psrd2d.x0;
var x1 = _psrd2d.x1;
var x2 = _psrd2d.x2;
var iu = _psrd2d.iu;
var iv = _psrd2d.iv;
var xw = _psrd2d.xw;
var yw = _psrd2d.yw;
var hash = _psrd2d.hash;
var psi = _psrd2d.psi;
var gx = _psrd2d.gx;
var gy = _psrd2d.gy;
var g0 = _psrd2d.g0;
var g1 = _psrd2d.g1;
var g2 = _psrd2d.g2;
var w = _psrd2d.w;
var w2 = _psrd2d.w2;
var w4 = _psrd2d.w4;
var gdotx = _psrd2d.gdotx;
// 2. Transform input point to find simplex "base" i0.
// x is [ x, y ], period is [ x, y ]
var xx = x[0];
var xy = x[1];
uv.x = xx + xy * 0.5;
uv.y = xy;
i0.x = Math.floor(uv.x);
i0.y = Math.floor(uv.y);
f0.x = uv.x - i0.x;
f0.y = uv.y - i0.y;
// 3. Enumerate simplex corners and transform back.
var cmp = (f0.y <= f0.x) ? 1 : 0;
o1.x = cmp;
o1.y = 1.0 - cmp;
i1.x = i0.x + o1.x;
i1.y = i0.y + o1.y;
i2.x = i0.x + 1.0;
i2.y = i0.y + 1.0;
v0.x = i0.x - i0.y * 0.5;
v0.y = i0.y;
v1.x = v0.x + o1.x - o1.y * 0.5;
v1.y = v0.y + o1.y;
v2.x = v0.x + 0.5;
v2.y = v0.y + 1.0;
// 4. Compute distances to corners before we wrap them.
x0.x = xx - v0.x; x0.y = xy - v0.y;
x1.x = xx - v1.x; x1.y = xy - v1.y;
x2.x = xx - v2.x; x2.y = xy - v2.y;
// 5, 6. wrap to period and adjust i0, i1, i2 accordingly.
var hasPeriod = (period[0] > 0) || (period[1] > 0);
if (hasPeriod)
{
xw[0] = v0.x; xw[1] = v1.x; xw[2] = v2.x;
yw[0] = v0.y; yw[1] = v1.y; yw[2] = v2.y;
if (period[0] > 0.0)
{
xw[0] = ((v0.x % period[0]) + period[0]) % period[0];
xw[1] = ((v1.x % period[0]) + period[0]) % period[0];
xw[2] = ((v2.x % period[0]) + period[0]) % period[0];
}
if (period[1] > 0.0)
{
yw[0] = ((v0.y % period[1]) + period[1]) % period[1];
yw[1] = ((v1.y % period[1]) + period[1]) % period[1];
yw[2] = ((v2.y % period[1]) + period[1]) % period[1];
}
for (i = 0; i < 3; i++)
{
iu[i] = Math.floor(xw[i] + 0.5 * yw[i] + 0.5);
iv[i] = Math.floor(yw[i] + 0.5);
}
}
else
{
iu[0] = i0.x; iu[1] = i1.x; iu[2] = i2.x;
iv[0] = i0.y; iv[1] = i1.y; iv[2] = i2.y;
}
// Custom: Seed the noise.
iu[0] += seed[0]; iu[1] += seed[0]; iu[2] += seed[0];
iv[0] += seed[1]; iv[1] += seed[1]; iv[2] += seed[1];
// 7. Compute the hash for each of the simplex corners.
for (i = 0; i < 3; i++)
{
hash[i] = iu[i] % 289.0;
if (hash[i] < 0) { hash[i] += 289.0; }
}
for (i = 0; i < 3; i++)
{
hash[i] = (((hash[i] * 51.0 + 2.0) * hash[i]) + iv[i]) % 289.0;
}
for (i = 0; i < 3; i++)
{
hash[i] = (((hash[i] * 34.0 + 10.0) * hash[i])) % 289.0;
}
// 8, 9a. Generate the gradients with an optional rotation.
for (i = 0; i < 3; i++)
{
psi[i] = hash[i] * 0.07482 + alpha;
gx[i] = Math.cos(psi[i]);
gy[i] = Math.sin(psi[i]);
}
g0.x = gx[0]; g0.y = gy[0];
g1.x = gx[1]; g1.y = gy[1];
g2.x = gx[2]; g2.y = gy[2];
// 10. Compute radial falloff.
w[0] = 0.8 - dot2(x0, x0);
w[1] = 0.8 - dot2(x1, x1);
w[2] = 0.8 - dot2(x2, x2);
for (i = 0; i < 3; i++)
{
w[i] = Math.max(w[i], 0.0);
w2[i] = w[i] * w[i];
w4[i] = w2[i] * w2[i];
}
// 11. Linear ramp along gradient (by a scalar product)
gdotx[0] = dot2(g0, x0);
gdotx[1] = dot2(g1, x1);
gdotx[2] = dot2(g2, x2);
// 12, 13. Multiply and sum up noise terms.
var n = w4[0] * gdotx[0] + w4[1] * gdotx[1] + w4[2] * gdotx[2];
// Scale the noise value to [-1,1] (empirical factor).
return 10.9 * n;
};
// --- 3D psrdnoise implementation (port of psrdnoise3.glsl) ---
var _psrd3d = {
// Temporary containers to avoid allocations.
uvw: [ 0, 0, 0 ],
i0: [ 0, 0, 0 ],
f0: [ 0, 0, 0 ],
g_: [ 0, 0, 0 ],
l_: [ 0, 0, 0 ],
g: [ 0, 0, 0 ],
l: [ 0, 0, 0 ],
o1: [ 0, 0, 0 ],
o2: [ 0, 0, 0 ],
i1: [ 0, 0, 0 ],
i2: [ 0, 0, 0 ],
i3: [ 0, 0, 0 ],
v0: [ 0, 0, 0 ],
v1: [ 0, 0, 0 ],
v2: [ 0, 0, 0 ],
v3: [ 0, 0, 0 ],
x0: [ 0, 0, 0 ],
x1: [ 0, 0, 0 ],
x2: [ 0, 0, 0 ],
x3: [ 0, 0, 0 ],
vx: [ 0, 0, 0, 0 ],
vy: [ 0, 0, 0, 0 ],
vz: [ 0, 0, 0, 0 ],
hash: [ 0, 0, 0, 0 ],
theta: [ 0, 0, 0, 0 ],
sz: [ 0, 0, 0, 0 ],
psi: [ 0, 0, 0, 0 ],
Ct: [ 0, 0, 0, 0 ],
St: [ 0, 0, 0, 0 ],
szp: [ 0, 0, 0, 0 ],
gx: [ 0, 0, 0, 0 ],
gy: [ 0, 0, 0, 0 ],
gz: [ 0, 0, 0, 0 ],
px: [ 0, 0, 0, 0 ],
py: [ 0, 0, 0, 0 ],
pz: [ 0, 0, 0, 0 ],
Sp: [ 0, 0, 0, 0 ],
Cp: [ 0, 0, 0, 0 ],
Ctp: [ 0, 0, 0, 0 ],
qx: [ 0, 0, 0, 0 ],
qy: [ 0, 0, 0, 0 ],
qz: [ 0, 0, 0, 0 ],
Sa: [ 0, 0, 0, 0 ],
Ca: [ 0, 0, 0, 0 ],
w: [ 0, 0, 0, 0 ],
w2: [ 0, 0, 0, 0 ],
w3: [ 0, 0, 0, 0 ],
gdotx: [ 0, 0, 0, 0 ]
};
var permute4 = function (i, out)
{
// out = mod(((im * 34.0) + 10.0) * im, 289.0);
for (var k = 0; k < 4; k++)
{
var im = i[k] % 289.0;
if (im < 0) { im += 289.0; }
out[k] = (((im * 34.0) + 10.0) * im) % 289.0;
}
};
var psrdnoise3d = function (x, period, alpha)
{
var uvw = _psrd3d.uvw;
var i0 = _psrd3d.i0;
var f0 = _psrd3d.f0;
var g_ = _psrd3d.g_;
var l_ = _psrd3d.l_;
var g = _psrd3d.g;
var l = _psrd3d.l;
var o1 = _psrd3d.o1;
var o2 = _psrd3d.o2;
var i1 = _psrd3d.i1;
var i2 = _psrd3d.i2;
var i3 = _psrd3d.i3;
var v0 = _psrd3d.v0;
var v1 = _psrd3d.v1;
var v2 = _psrd3d.v2;
var v3 = _psrd3d.v3;
var x0 = _psrd3d.x0;
var x1 = _psrd3d.x1;
var x2 = _psrd3d.x2;
var x3 = _psrd3d.x3;
var vx = _psrd3d.vx;
var vy = _psrd3d.vy;
var vz = _psrd3d.vz;
var hash = _psrd3d.hash;
var theta = _psrd3d.theta;
var sz = _psrd3d.sz;
var psi = _psrd3d.psi;
var Ct = _psrd3d.Ct;
var St = _psrd3d.St;
var szp = _psrd3d.szp;
var gx = _psrd3d.gx;
var gy = _psrd3d.gy;
var gz = _psrd3d.gz;
var px = _psrd3d.px;
var py = _psrd3d.py;
var pz = _psrd3d.pz;
var Sp = _psrd3d.Sp;
var Cp = _psrd3d.Cp;
var Ctp = _psrd3d.Ctp;
var qx = _psrd3d.qx;
var qy = _psrd3d.qy;
var qz = _psrd3d.qz;
var Sa = _psrd3d.Sa;
var Ca = _psrd3d.Ca;
var w = _psrd3d.w;
var w2 = _psrd3d.w2;
var w3 = _psrd3d.w3;
var gdotx = _psrd3d.gdotx;
// 2. Transform to simplex space and find simplex "base" i0.
// uvw = M * x, where M is the 3x3 matrix in the GLSL code.
uvw[0] = 0 * x[0] + 1 * x[1] + 1 * x[2];
uvw[1] = 1 * x[0] + 0 * x[1] + 1 * x[2];
uvw[2] = 1 * x[0] + 1 * x[1] + 0 * x[2];
for (var i = 0; i < 3; i++)
{
i0[i] = Math.floor(uvw[i]);
f0[i] = uvw[i] - i0[i];
}
// 3. Enumerate simplex corners and transform back.
// g_ = step(f0.xyx, f0.yzz);
g_[0] = (f0[0] > f0[1]) ? 0 : 1;
g_[1] = (f0[1] > f0[2]) ? 0 : 1;
g_[2] = (f0[0] > f0[2]) ? 0 : 1;
l_[0] = 1.0 - g_[0];
l_[1] = 1.0 - g_[1];
l_[2] = 1.0 - g_[2];
g[0] = l_[2];
g[1] = g_[0];
g[2] = g_[1];
l[0] = l_[0];
l[1] = l_[1];
l[2] = g_[2];
for (i = 0; i < 3; i++)
{
o1[i] = Math.min(g[i], l[i]);
o2[i] = Math.max(g[i], l[i]);
}
for (i = 0; i < 3; i++)
{
i1[i] = i0[i] + o1[i];
i2[i] = i0[i] + o2[i];
i3[i] = i0[i] + 1.0;
}
// v* = Mi * i*, where Mi is the inverse matrix.
// Mi rows: [-0.5, 0.5, 0.5]
// [ 0.5, -0.5, 0.5]
// [ 0.5, 0.5, -0.5]
var ix0 = i0[0];
var iy0 = i0[1];
var iz0 = i0[2];
var ix1 = i1[0];
var iy1 = i1[1];
var iz1 = i1[2];
var ix2 = i2[0];
var iy2 = i2[1];
var iz2 = i2[2];
var ix3 = i3[0];
var iy3 = i3[1];
var iz3 = i3[2];
v0[0] = -0.5 * ix0 + 0.5 * iy0 + 0.5 * iz0;
v0[1] = 0.5 * ix0 - 0.5 * iy0 + 0.5 * iz0;
v0[2] = 0.5 * ix0 + 0.5 * iy0 - 0.5 * iz0;
v1[0] = -0.5 * ix1 + 0.5 * iy1 + 0.5 * iz1;
v1[1] = 0.5 * ix1 - 0.5 * iy1 + 0.5 * iz1;
v1[2] = 0.5 * ix1 + 0.5 * iy1 - 0.5 * iz1;
v2[0] = -0.5 * ix2 + 0.5 * iy2 + 0.5 * iz2;
v2[1] = 0.5 * ix2 - 0.5 * iy2 + 0.5 * iz2;
v2[2] = 0.5 * ix2 + 0.5 * iy2 - 0.5 * iz2;
v3[0] = -0.5 * ix3 + 0.5 * iy3 + 0.5 * iz3;
v3[1] = 0.5 * ix3 - 0.5 * iy3 + 0.5 * iz3;
v3[2] = 0.5 * ix3 + 0.5 * iy3 - 0.5 * iz3;
// 4. Compute distances to corners before we wrap.
for (i = 0; i < 3; i++)
{
x0[i] = x[i] - v0[i];
x1[i] = x[i] - v1[i];
x2[i] = x[i] - v2[i];
x3[i] = x[i] - v3[i];
}
// 5, 6. Wrap to periods and update i0, i1, i2, i3 accordingly.
var hasPeriod = (period[0] > 0) || (period[1] > 0) || (period[2] > 0);
if (hasPeriod)
{
vx[0] = v0[0]; vx[1] = v1[0]; vx[2] = v2[0]; vx[3] = v3[0];
vy[0] = v0[1]; vy[1] = v1[1]; vy[2] = v2[1]; vy[3] = v3[1];
vz[0] = v0[2]; vz[1] = v1[2]; vz[2] = v2[2]; vz[3] = v3[2];
if (period[0] > 0.0)
{
for (i = 0; i < 4; i++)
{
vx[i] = ((vx[i] % period[0]) + period[0]) % period[0];
}
}
if (period[1] > 0.0)
{
for (i = 0; i < 4; i++)
{
vy[i] = ((vy[i] % period[1]) + period[1]) % period[1];
}
}
if (period[2] > 0.0)
{
for (i = 0; i < 4; i++)
{
vz[i] = ((vz[i] % period[2]) + period[2]) % period[2];
}
}
// Recompute lattice coordinates from wrapped positions.
// i* = floor(M * v* + 0.5);
var rx0 = vx[0];
var ry0 = vy[0];
var rz0 = vz[0];
var rx1 = vx[1];
var ry1 = vy[1];
var rz1 = vz[1];
var rx2 = vx[2];
var ry2 = vy[2];
var rz2 = vz[2];
var rx3 = vx[3];
var ry3 = vy[3];
var rz3 = vz[3];
i0[0] = Math.floor(0 * rx0 + 1 * ry0 + 1 * rz0 + 0.5);
i0[1] = Math.floor(1 * rx0 + 0 * ry0 + 1 * rz0 + 0.5);
i0[2] = Math.floor(1 * rx0 + 1 * ry0 + 0 * rz0 + 0.5);
i1[0] = Math.floor(0 * rx1 + 1 * ry1 + 1 * rz1 + 0.5);
i1[1] = Math.floor(1 * rx1 + 0 * ry1 + 1 * rz1 + 0.5);
i1[2] = Math.floor(1 * rx1 + 1 * ry1 + 0 * rz1 + 0.5);
i2[0] = Math.floor(0 * rx2 + 1 * ry2 + 1 * rz2 + 0.5);
i2[1] = Math.floor(1 * rx2 + 0 * ry2 + 1 * rz2 + 0.5);
i2[2] = Math.floor(1 * rx2 + 1 * ry2 + 0 * rz2 + 0.5);
i3[0] = Math.floor(0 * rx3 + 1 * ry3 + 1 * rz3 + 0.5);
i3[1] = Math.floor(1 * rx3 + 0 * ry3 + 1 * rz3 + 0.5);
i3[2] = Math.floor(1 * rx3 + 1 * ry3 + 0 * rz3 + 0.5);
}
// Custom: Seed the noise.
i0[0] += seed[0]; i0[1] += seed[1]; i0[2] += seed[2];
i1[0] += seed[0]; i1[1] += seed[1]; i1[2] += seed[2];
i2[0] += seed[0]; i2[1] += seed[1]; i2[2] += seed[2];
i3[0] += seed[0]; i3[1] += seed[1]; i3[2] += seed[2];
// 7. Compute hash for each of the four corners.
// hash = permute(permute(permute(vec4(i0.z, i1.z, i2.z, i3.z)) + vec4(i0.y, i1.y, i2.y, i3.y)) + vec4(i0.x, i1.x, i2.x, i3.x));
var t0 = _psrd3d.vx; // reuse as temp vec4
var t1 = _psrd3d.vy; // reuse as temp vec4
t0[0] = i0[2]; t0[1] = i1[2]; t0[2] = i2[2]; t0[3] = i3[2];
permute4(t0, t1);
t1[0] += i0[1];
t1[1] += i1[1];
t1[2] += i2[1];
t1[3] += i3[1];
permute4(t1, t0);
t0[0] += i0[0];
t0[1] += i1[0];
t0[2] += i2[0];
t0[3] += i3[0];
permute4(t0, hash);
// 8. Compute rotating gradients using the "well-behaved" method.
for (i = 0; i < 4; i++)
{
theta[i] = hash[i] * 3.883222077;
sz[i] = 0.996539792 - 0.006920415 * hash[i];
psi[i] = hash[i] * 0.108705628;
Ct[i] = Math.cos(theta[i]);
St[i] = Math.sin(theta[i]);
szp[i] = Math.sqrt(Math.max(0, 1.0 - sz[i] * sz[i]));
}
if (alpha !== 0.0)
{
for (i = 0; i < 4; i++)
{
px[i] = Ct[i] * szp[i];
py[i] = St[i] * szp[i];
pz[i] = sz[i];
Sp[i] = Math.sin(psi[i]);
Cp[i] = Math.cos(psi[i]);
Ctp[i] = St[i] * Sp[i] - Ct[i] * Cp[i];
qx[i] = (1 - sz[i]) * (Ctp[i] * St[i]) + sz[i] * Sp[i];
qy[i] = (1 - sz[i]) * (-Ctp[i] * Ct[i]) + sz[i] * Cp[i];
qz[i] = -(py[i] * Cp[i] + px[i] * Sp[i]);
Sa[i] = Math.sin(alpha);
Ca[i] = Math.cos(alpha);
gx[i] = Ca[i] * px[i] + Sa[i] * qx[i];
gy[i] = Ca[i] * py[i] + Sa[i] * qy[i];
gz[i] = Ca[i] * pz[i] + Sa[i] * qz[i];
}
}
else
{
for (i = 0; i < 4; i++)
{
gx[i] = Ct[i] * szp[i];
gy[i] = St[i] * szp[i];
gz[i] = sz[i];
}
}
// 10. Compute radial falloff.
for (i = 0; i < 4; i++)
{
var dx0 = (i === 0) ? x0[0] : (i === 1) ? x1[0] : (i === 2) ? x2[0] : x3[0];
var dy0 = (i === 0) ? x0[1] : (i === 1) ? x1[1] : (i === 2) ? x2[1] : x3[1];
var dz0 = (i === 0) ? x0[2] : (i === 1) ? x1[2] : (i === 2) ? x2[2] : x3[2];
var dot = dx0 * dx0 + dy0 * dy0 + dz0 * dz0;
w[i] = 0.5 - dot;
if (w[i] < 0.0)
{
w[i] = 0.0;
}
w2[i] = w[i] * w[i];
w3[i] = w2[i] * w[i];
}
// 11. Linear ramps along gradients (by scalar product)
for (i = 0; i < 4; i++)
{
var gxv = gx[i];
var gyv = gy[i];
var gzv = gz[i];
var vx0 = (i === 0) ? x0[0] : (i === 1) ? x1[0] : (i === 2) ? x2[0] : x3[0];
var vy0 = (i === 0) ? x0[1] : (i === 1) ? x1[1] : (i === 2) ? x2[1] : x3[1];
var vz0 = (i === 0) ? x0[2] : (i === 1) ? x1[2] : (i === 2) ? x2[2] : x3[2];
gdotx[i] = gxv * vx0 + gyv * vy0 + gzv * vz0;
}
// 12, 13. Multiply and sum up the four noise terms.
var n = 0;
for (i = 0; i < 4; i++)
{
n += w3[i] * gdotx[i];
}
// Scale noise value to range [-1,1] (empirical factor).
return 39.5 * n;
};
module.exports = HashSimplex;