mapspinner 0.1.25 → 0.1.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapspinner",
3
- "version": "0.1.25",
3
+ "version": "0.1.27",
4
4
  "description": "WebGL2 Earth-scale terrain rendering SDK for interactive globe applications",
5
5
  "main": "src/index.js",
6
6
  "exports": {
package/src/gl-render.js CHANGED
@@ -291,6 +291,13 @@ export async function initMapspinnerRender(gl, opts = {}) {
291
291
  // FXC unroll-defeat (2026-06-12 AMD d3d11 fix): runtime octave bound for broadShapeM; the shader
292
292
  // guards uOctMax<=0 -> 12, so this set is belt-and-braces. Live dial: window.__octMax.
293
293
  gl.uniform1i(loc('uOctMax'), (typeof window!=='undefined' && window.__octMax!=null) ? (window.__octMax|0) : 12);
294
+ gl.uniform1i(loc('uInciseRidgeOcts'), (typeof window!=='undefined' && window.__inciseRidgeOcts!=null) ? (window.__inciseRidgeOcts|0) : 4);
295
+ gl.uniform1i(loc('uBroadLowOcts'), (typeof window!=='undefined' && window.__broadLowOcts!=null) ? (window.__broadLowOcts|0) : 8);
296
+ gl.uniform1i(loc('uPeakOcts'), (typeof window!=='undefined' && window.__peakOcts!=null) ? (window.__peakOcts|0) : 3);
297
+ gl.uniform1i(loc('uVtxBaseOcts'), (typeof window!=='undefined' && window.__vtxBaseOcts!=null) ? (window.__vtxBaseOcts|0) : 6);
298
+ gl.uniform1i(loc('uVtxErodeOcts'), (typeof window!=='undefined' && window.__vtxErodeOcts!=null) ? (window.__vtxErodeOcts|0) : 4);
299
+ gl.uniform1i(loc('uDetailFbmOcts'), (typeof window!=='undefined' && window.__detailFbmOcts!=null) ? (window.__detailFbmOcts|0) : 3);
300
+ gl.uniform1i(loc('uFSDetailOcts'), (typeof window!=='undefined' && window.__fsDetailOcts!=null) ? (window.__fsDetailOcts|0) : 3);
294
301
  // FXC fold-defeat (2026-06-12, the rock-on-flat patches): the lit-normal FD step is uniform-fed
295
302
  // so d3d11/FXC cannot constant-fold the 150/R offset. Live dial: window.__nrmStepM.
296
303
  gl.uniform1f(loc('uNrmStepM'), g('nrmStepM', 150.0));
@@ -497,7 +497,7 @@ export async function initMapspinnerPlanet(gl, opts = {}) {
497
497
  // ONLY to splitDist (NOT distF below): distF = sf*8.0 is the altitude-weighting term, and scaling sf
498
498
  // into BOTH compounds into ~2 levels at altitude (witnessed: base sf*2 gave mx 5->7 at 8000km). Keep
499
499
  // distF on the original sf so the push is a clean ONE step. maxLevel (16) is untouched -- no new LOD.
500
- const LOD_STEP = 3.0; // push ALL LODs 0.75 step (reduced from 4.0 to target ~600 quads at all alts):
500
+ const LOD_STEP = 2.3; // push ALL LODs ~0.6 step (reduced from 3.0 to target ~600 quads at all alts):
501
501
  qt.computeSplitDist(sf * LOD_STEP, gl.drawingBufferHeight || 480, fovy);
502
502
  // DETAIL-FARTHER (user 2026-06-01h: each LOD pop should happen ~3x farther out -- 6km detail at
503
503
  // 24km, etc.). A quad subdivides when camAlt < l * splitDist * distFactor, so tripling distFactor
@@ -549,6 +549,12 @@ export async function initMapspinnerPlanet(gl, opts = {}) {
549
549
  }
550
550
  const _maxLevelOverridden = (typeof window !== 'undefined' && window.__maxLevel != null);
551
551
  if (!_maxLevelOverridden && altKm < DECK_CAP_ALT_KM && mxl > DECK_CAP_LEVEL) mxl = DECK_CAP_LEVEL;
552
+ // HIGH-ALT LOD DROP (user 2026-06-13): at 30km+ the finest LOD is sub-pixel.
553
+ // Drop 1 level at 30km, 2 levels at 700km+ so the next-coarser LOD naturally expands
554
+ // (cells 2x larger). Skipped when window.__maxLevel is set.
555
+ if (!_maxLevelOverridden && altKm >= 30 && mxl > 4) {
556
+ mxl -= (altKm >= 700) ? 2 : 1;
557
+ }
552
558
  qt.setConfig(R, mxl, distF);
553
559
  const splitDist = sf + 1.0;
554
560
 
@@ -165,10 +165,22 @@ float lakeCarveM(vec3 dir){ float w; return lakeCarveM(dir, w); }
165
165
  // differently -> the summed ridged crests run in EVERY direction, not just the x/y/z lattice H/V.
166
166
  // The matrix rows are a fixed tilted basis (~no axis alignment); det ~= 1 so it doesn't drift scale.
167
167
  const mat3 OCT_ROT = mat3( 0.80, 0.36, -0.48, -0.48, 0.86, -0.18, 0.36, 0.36, 0.86);
168
+ // FXC UNROLL-DEFEAT runtime-bounded loops (2026-06-12): uniform int guards prevent FXC from
169
+ // fully unrolling these loops (which triggers mis-translation on AMD D3D11). Each guard falls
170
+ // back to the original octave count when the uniform is 0 or unset (e.g. probe program).
171
+ uniform int uOctMax; // broadShapeM octave count (12); runtime-bound to defeat FXC unrolling
172
+ uniform int uInciseRidgeOcts; // inciseRidgeField octave count (4); runtime-bound to defeat FXC unrolling
173
+ uniform int uBroadLowOcts; // broadShapeLowM octave count (8); runtime-bound to defeat FXC unrolling
174
+ uniform int uPeakOcts; // broadShapeM peak crest octave count (3); runtime-bound to defeat FXC unrolling
175
+ uniform int uVtxBaseOcts; // vtxDisplace base fBm octave count (6); runtime-bound to defeat FXC unrolling
176
+ uniform int uVtxErodeOcts; // vtxDisplace mountain erosion octave count (4); runtime-bound to defeat FXC unrolling
177
+ uniform int uDetailFbmOcts; // detailFbm octave count (3); runtime-bound to defeat FXC unrolling
178
+ uniform int uFSDetailOcts; // FS detail overlay octave count (3); runtime-bound to defeat FXC unrolling
168
179
  float inciseRidgeField(vec3 d, float baseFreq, float freqMul){
169
180
  vec3 p = d;
170
181
  float freq = baseFreq, amp = 1.0, sum = 0.0, norm = 0.0;
171
- for (int o = 0; o < 3; o++){ // 4->3 octaves (max-speed sweep 2026-06-10: finest ~6km gullies sub-pixel at flight altitudes; -1 tap x4 calls x3 composeHeight/vertex)
182
+ int irOcts = (uInciseRidgeOcts > 0) ? uInciseRidgeOcts : 4;
183
+ for (int o = 0; o < irOcts; o++){
172
184
  sum += amp * (1.0 - abs(snoise3(p * freq)));
173
185
  norm += amp; freq *= freqMul; amp *= 0.5; p = OCT_ROT * p; // rotate domain each octave
174
186
  }
@@ -327,7 +339,8 @@ highp float broadShapeLowM(vec3 dir){ // W7: metres (~13000) + freq (~49152) a
327
339
  if (hasHpf == 0) return 0.0;
328
340
  vec3 d = normalize(dir);
329
341
  highp float amp = 6500.0, freq = 3.0, sum = 0.0;
330
- for (int o=0; o<8; o++){ sum += amp * snoise3(d*freq); amp *= (o < 6 ? 0.66 : 0.82); freq *= 2.0; }
342
+ int blOcts = (uBroadLowOcts > 0) ? uBroadLowOcts : 8;
343
+ for (int o=0; o<blOcts; o++){ sum += amp * snoise3(d*freq); amp *= (o < 6 ? 0.66 : 0.82); freq *= 2.0; }
331
344
  return sum - 900.0;
332
345
  }
333
346
  // ---- SHARED micro-relief helpers (MOVED to the common preamble, THC-Normal W1, so composeHeight()
@@ -369,13 +382,6 @@ highp vec2 faceWarp(highp vec2 p){ return defRadius * tan((p / defRadius) * 0.78
369
382
  // PERLIN-EVERYWHERE lever -- shared by the composeHeight elevation term (VS/PROBE) and the FS albedo
370
383
  // overlay, so it must be declared in ALL stages (outside the VS/PROBE guard below).
371
384
  uniform float uDetailOverlay; // amplitude lever (user-tuned 6; 0 = off; __detailOverlay)
372
- // FXC UNROLL-DEFEAT (2026-06-12, 'rocks everywhere + no normals on AMD default Chrome'): ANGLE's
373
- // D3D11 backend runs FXC, which fully unrolls constant-bound loops and applies aggressive math
374
- // reordering across the unrolled body -- the long-suspected mis-translation domain (vulkan on the
375
- // SAME AMD GPU renders correctly; d3d11 does not). A runtime loop bound cannot be unrolled. The
376
- // uniform is set to 12 by gl-render; the max(...) guard keeps an unset uniform (0) from flattening
377
- // the planet -- worst case the loop still runs the literal octave count.
378
- uniform int uOctMax; // broadShapeM octave count (12); runtime-bound to defeat FXC unrolling
379
385
  uniform float uNrmStepM; // lit-normal FD step in metres (150); uniform-fed to defeat FXC constant folding
380
386
  #if defined(_VERTEX_) || defined(_PROBE_)
381
387
  highp float broadShapeM(vec3 dir, float reliefMul, float ridgeMul){ // W7: returns metres (~13000) -> highp
@@ -447,7 +453,8 @@ highp float broadShapeM(vec3 dir, float reliefMul, float ridgeMul){ // W7: ret
447
453
  // with the braids, the dark raw-photo far field, and the UDN normal-frame bug all fixed, the
448
454
  // original steep terrain stands.
449
455
  highp float pf = 200.0; float pa = 1.0, ps = 0.0, pn = 0.0; // W7: pf (~4100) feeds the noise lattice -> highp
450
- for (int o = 0; o < 3; o++) { // 5->3 octaves (max-speed sweep: -6 taps/vertex in the belt; coarse 2 set the peak stance, decay 0.5->0.65 reweights)
456
+ int pkOcts = (uPeakOcts > 0) ? uPeakOcts : 3;
457
+ for (int o = 0; o < pkOcts; o++) {
451
458
  ps += pa * (1.0 - abs(snoise3(d * pf + vec3(3.3, 7.7, 1.1))));
452
459
  pn += pa; pa *= 0.65; pf *= 2.13;
453
460
  }
@@ -524,7 +531,8 @@ float vtxDisplace(highp vec2 fp, float tileM, float rugged){ // W7: fp = face-
524
531
  // A coarse leaf's 16-cell mesh simply under-samples the finest octaves (smooth interpolation, not a step);
525
532
  // the amplitude is small (amp0 8m * uHiFreqCut 0.25 * ruggedAmp) so any residual aliasing is minor and
526
533
  // far less visible than the per-patch steps it removes. (tileM kept in the signature for callers.)
527
- for (int o=0; o<6; o++){
534
+ int vbOcts = (uVtxBaseOcts > 0) ? uVtxBaseOcts : 6;
535
+ for (int o=0; o<vbOcts; o++){
528
536
  float n = vnoise2(fp / wl); // [-1,1]
529
537
  sumF += a * n; // smooth fBm
530
538
  float r = 1.0 - abs(n); r *= r; // ridged: sharp crest at n=0
@@ -543,7 +551,8 @@ float vtxDisplace(highp vec2 fp, float tileM, float rugged){ // W7: fp = face-
543
551
  float mtnGate = smoothstep(0.55, 1.0, rugged); // ramps in over the mountain belt (rugged ~ elevAmp)
544
552
  if (mtnGate > 0.0) {
545
553
  float ea = 2.5, ewl = 2880.0, esumF = 0.0, esumR = 0.0; // user 2026-06-09: HALF the erosion elevation (5->2.5m) + 3x WIDER again (960->2880m)
546
- for (int o = 0; o < 2; o++) { // 4->2 octaves (max-speed sweep: 720/360m fine erosion sub-vertex at GRID 16; coarse 2880/1440m carry the gully shape)
554
+ int veOcts = (uVtxErodeOcts > 0) ? uVtxErodeOcts : 4;
555
+ for (int o = 0; o < veOcts; o++) {
547
556
  float en = vnoise2(fp / ewl);
548
557
  esumF += ea * en;
549
558
  float er = 1.0 - abs(en); er *= er;
@@ -561,8 +570,9 @@ float vtxDisplace(highp vec2 fp, float tileM, float rugged){ // W7: fp = face-
561
570
  // relief variation correlate, reading as one landform). World-dir keyed, seam-safe.
562
571
  highp float detailFbm(vec3 dir) {
563
572
  float ov = 0.0, oa = 0.0;
564
- float fq = 150.0, am = 1.0; // octaves: ~42km / 8.5km (3rd 1.7km octave dropped, max-speed sweep: sub-pixel at altitude, -3 taps/vertex)
565
- for (int o = 0; o < 2; o++) {
573
+ float fq = 150.0, am = 1.0;
574
+ int dfOcts = (uDetailFbmOcts > 0) ? uDetailFbmOcts : 3;
575
+ for (int o = 0; o < dfOcts; o++) {
566
576
  ov += am * snoise3(dir * fq + vec3(float(o) * 7.3));
567
577
  oa += am;
568
578
  fq *= 5.0; am *= 0.75;
@@ -1371,7 +1381,8 @@ vec3 terrainAlbedoClimate(float h, float slope, float rockSlope, float temp, flo
1371
1381
  highp vec3 od = normalize(worldPos);
1372
1382
  float ov = 0.0, oa = 0.0;
1373
1383
  float fq = 150.0, am = 1.0; // octaves: ~42km / 8.5km / 1.7km features
1374
- for (int o = 0; o < 3; o++) {
1384
+ int fdOcts = (uFSDetailOcts > 0) ? uFSDetailOcts : 3;
1385
+ for (int o = 0; o < fdOcts; o++) {
1375
1386
  float wl = 40000000.0 / fq; // feature wavelength (m) ~ 2*pi*R / fq
1376
1387
  float nyq = 1.0 - smoothstep(wl * 0.03, wl * 0.12, pxWorld); // fade before sub-pixel
1377
1388
  ov += am * nyq * snoise3(od * fq + vec3(float(o) * 7.3));