Upload index.html
Browse files- index.html +30 -15
index.html
CHANGED
|
@@ -102,7 +102,7 @@ function waveH(x, z, t){
|
|
| 102 |
shader is handed the nearby ones as data, so the two can never disagree about whether land
|
| 103 |
is there at all. The profile below is mirrored in the shader, so the boat sits on the same
|
| 104 |
ground it is drawn on. */
|
| 105 |
-
const ICELL = 48, MAX_ISLANDS =
|
| 106 |
const LAND = { beach: 0.02, shelf: 1.2, deep: 6.0 }; // beach: how far up the shore the boat may sit
|
| 107 |
let SEED = 1337;
|
| 108 |
|
|
@@ -120,11 +120,18 @@ function frac(v){ return v - Math.floor(v); }
|
|
| 120 |
const F32 = v => Math.fround(v);
|
| 121 |
function lhash(x, z){ return (ihash((Math.imul(x, 3419) + Math.imul(z, 2147483647)) >>> 0) & 1023) / 1023; }
|
| 122 |
function lnoise(px, pz){ // takes lattice coordinates, so the caller sets the scale
|
| 123 |
-
|
| 124 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
const a = lhash(ix, iz), b = lhash(ix+1, iz), c = lhash(ix, iz+1), d = lhash(ix+1, iz+1);
|
| 126 |
-
const u = a + (b
|
| 127 |
-
|
|
|
|
| 128 |
}
|
| 129 |
|
| 130 |
/* code packs four 4-bit fields, because that is the part both sides can decode identically:
|
|
@@ -154,13 +161,13 @@ function islandAt(cx, cz){
|
|
| 154 |
}
|
| 155 |
function decode(code){
|
| 156 |
const pk = code & 15, sh = (code >> 4) & 15, rg = (code >> 8) & 15, type = code >> 12;
|
| 157 |
-
let peak = 1.2 + pk*0.22, shape = 1.2 + sh*0.03, ramp = 0;
|
| 158 |
-
// shape is the exponent on the falloff, so a small shape is nearly flat
|
| 159 |
-
//
|
| 160 |
-
//
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
return { peak, shape, rough: F32(rg/15), type, ramp, pk, sh, rg };
|
| 164 |
}
|
| 165 |
|
| 166 |
function landH(x, z){
|
|
@@ -207,7 +214,13 @@ function islandsNear(minX, maxX, minZ, maxZ){
|
|
| 207 |
for(let cz = Math.floor(c/ICELL); cz <= Math.floor(d/ICELL) && out.length < MAX_ISLANDS; cz++)
|
| 208 |
for(let cx = Math.floor(a/ICELL); cx <= Math.floor(b/ICELL) && out.length < MAX_ISLANDS; cx++){
|
| 209 |
const isl = islandAt(cx, cz);
|
| 210 |
-
if(isl)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 211 |
}
|
| 212 |
return out;
|
| 213 |
}
|
|
@@ -232,7 +245,7 @@ float fbm(vec2 p, int oct){
|
|
| 232 |
`;
|
| 233 |
|
| 234 |
const ISLANDS_GLSL = `
|
| 235 |
-
uniform vec4 uIsl[
|
| 236 |
uniform int uCount;
|
| 237 |
uint ihash(uint n){
|
| 238 |
n ^= n >> 15; n *= 0x2c1b3c9du; n ^= n >> 12; n *= 0x297a2d39u; n ^= n >> 15;
|
|
@@ -261,7 +274,9 @@ float landH(vec2 p){
|
|
| 261 |
float rr = isl.z * (0.55 + 0.45*wob);
|
| 262 |
if(d > rr) continue;
|
| 263 |
float peak = 1.2 + pk*0.22, shape = 1.2 + sh*0.03, ramp = 0.0;
|
| 264 |
-
if(type == 1.0){ peak = 2.0 + pk*0.35; }
|
|
|
|
|
|
|
| 265 |
else if(type == 2.0){ peak = 14.0 + pk*1.6; shape = 0.04 + sh*0.012; }
|
| 266 |
float rough = rg/15.0;
|
| 267 |
float k = 1.0 - d/rr;
|
|
|
|
| 102 |
shader is handed the nearby ones as data, so the two can never disagree about whether land
|
| 103 |
is there at all. The profile below is mirrored in the shader, so the boat sits on the same
|
| 104 |
ground it is drawn on. */
|
| 105 |
+
const ICELL = 48, MAX_ISLANDS = 96, MAXR = 70; // a 170 m grid plus the reach of the largest island
|
| 106 |
const LAND = { beach: 0.02, shelf: 1.2, deep: 6.0 }; // beach: how far up the shore the boat may sit
|
| 107 |
let SEED = 1337;
|
| 108 |
|
|
|
|
| 120 |
const F32 = v => Math.fround(v);
|
| 121 |
function lhash(x, z){ return (ihash((Math.imul(x, 3419) + Math.imul(z, 2147483647)) >>> 0) & 1023) / 1023; }
|
| 122 |
function lnoise(px, pz){ // takes lattice coordinates, so the caller sets the scale
|
| 123 |
+
// Every step is rounded to float32 so this matches what the shader does. The smoothstep and the two
|
| 124 |
+
// mixes were previously done in double, which is good enough for a metre grid - but the boat's height
|
| 125 |
+
// has to agree with the ground it is drawn on, and the only thing that can put the boat on a
|
| 126 |
+
// different height than the mesh is the boat and the shader disagreeing here.
|
| 127 |
+
const ix = Math.floor(px), iz = Math.floor(pz);
|
| 128 |
+
let fx = F32(px - F32(ix)), fz = F32(pz - F32(iz));
|
| 129 |
+
fx = F32(F32(F32(fx)*F32(fx))*F32(F32(3.0) - F32(F32(2.0)*fx)));
|
| 130 |
+
fz = F32(F32(F32(fz)*F32(fz))*F32(F32(3.0) - F32(F32(2.0)*fz)));
|
| 131 |
const a = lhash(ix, iz), b = lhash(ix+1, iz), c = lhash(ix, iz+1), d = lhash(ix+1, iz+1);
|
| 132 |
+
const u = F32(F32(a) + F32(F32(F32(b) - F32(a))*F32(fx)));
|
| 133 |
+
const v = F32(F32(c) + F32(F32(F32(d) - F32(c))*F32(fx)));
|
| 134 |
+
return F32(F32(u) + F32(F32(F32(v) - F32(u))*F32(fz)));
|
| 135 |
}
|
| 136 |
|
| 137 |
/* code packs four 4-bit fields, because that is the part both sides can decode identically:
|
|
|
|
| 161 |
}
|
| 162 |
function decode(code){
|
| 163 |
const pk = code & 15, sh = (code >> 4) & 15, rg = (code >> 8) & 15, type = code >> 12;
|
| 164 |
+
let peak = F32(F32(1.2) + F32(F32(pk)*F32(0.22))), shape = F32(F32(1.2) + F32(F32(sh)*F32(0.03))), ramp = 0.0;
|
| 165 |
+
// shape is the exponent on the falloff, so a small shape is nearly flat. A flat island instead uses a
|
| 166 |
+
// ramp - flat for most of the radius, then a straight fall over the last fraction of it, which is the
|
| 167 |
+
// beach the boat can actually sit on. Constants are rounded to float32 because the shader computes this
|
| 168 |
+
// in float32, and the two have to produce the same number for the same island.
|
| 169 |
+
if(type === 1){ peak = F32(F32(2.0) + F32(F32(pk)*F32(0.35))); ramp = F32(F32(0.10) + F32(F32(sh)*F32(0.04))); }
|
| 170 |
+
return { peak, shape, rough: F32(F32(rg)/F32(15)), type, ramp, pk, sh, rg };
|
| 171 |
}
|
| 172 |
|
| 173 |
function landH(x, z){
|
|
|
|
| 214 |
for(let cz = Math.floor(c/ICELL); cz <= Math.floor(d/ICELL) && out.length < MAX_ISLANDS; cz++)
|
| 215 |
for(let cx = Math.floor(a/ICELL); cx <= Math.floor(b/ICELL) && out.length < MAX_ISLANDS; cx++){
|
| 216 |
const isl = islandAt(cx, cz);
|
| 217 |
+
if(!isl) continue;
|
| 218 |
+
// Only islands that can actually reach a vertex inside the area the mesh is drawing. Without this
|
| 219 |
+
// the list filled up with islands that cannot affect any drawn point, so the ones dropped by the
|
| 220 |
+
// cap were still known to the boat - and that is a collision with land you cannot see.
|
| 221 |
+
const nx = Math.max(Math.min(isl.x, maxX), minX), nz = Math.max(Math.min(isl.z, maxZ), minZ);
|
| 222 |
+
if(Math.hypot(nx - isl.x, nz - isl.z) > isl.r) continue;
|
| 223 |
+
out.push(isl);
|
| 224 |
}
|
| 225 |
return out;
|
| 226 |
}
|
|
|
|
| 245 |
`;
|
| 246 |
|
| 247 |
const ISLANDS_GLSL = `
|
| 248 |
+
uniform vec4 uIsl[96]; // must match MAX_ISLANDS, otherwise islands past the cap are silently dropped
|
| 249 |
uniform int uCount;
|
| 250 |
uint ihash(uint n){
|
| 251 |
n ^= n >> 15; n *= 0x2c1b3c9du; n ^= n >> 12; n *= 0x297a2d39u; n ^= n >> 15;
|
|
|
|
| 274 |
float rr = isl.z * (0.55 + 0.45*wob);
|
| 275 |
if(d > rr) continue;
|
| 276 |
float peak = 1.2 + pk*0.22, shape = 1.2 + sh*0.03, ramp = 0.0;
|
| 277 |
+
if(type == 1.0){ peak = 2.0 + pk*0.35; ramp = 0.10 + sh*0.04; } // ramp was never set here, so the
|
| 278 |
+
// shader drew a dome while the boat walked into a flat top with a beach band - two different shapes
|
| 279 |
+
// for the same island, which is the collision near the large ones.
|
| 280 |
else if(type == 2.0){ peak = 14.0 + pk*1.6; shape = 0.04 + sh*0.012; }
|
| 281 |
float rough = rg/15.0;
|
| 282 |
float k = 1.0 - d/rr;
|