One commited on
Commit
873fee2
·
verified ·
1 Parent(s): a5b4831

Upload index.html

Browse files
Files changed (1) hide show
  1. 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 = 48, 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,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
- const ix = Math.floor(px), iz = Math.floor(pz), fx = px-ix, fz = pz-iz;
124
- const sx = fx*fx*(3 - 2*fx), sz = fz*fz*(3 - 2*fz);
 
 
 
 
 
 
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-a)*sx, v = c + (d-c)*sx;
127
- return u + (v-u)*sz;
 
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: a pinnacle gets near
159
- // vertical sides. A flat island instead uses a ramp - flat for most of the radius, then a
160
- // straight fall over the last fraction of it, which is the beach the boat can actually sit on.
161
- if(type === 1){ peak = 2.0 + pk*0.35; ramp = 0.10 + sh*0.04; }
162
- else if(type === 2){ peak = 14.0 + pk*1.6; shape = 0.04 + sh*0.012; }
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) out.push(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[48]; // must match MAX_ISLANDS, otherwise islands past 25 are silently dropped
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;