mapspinner 0.1.24 → 0.1.26

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.24",
3
+ "version": "0.1.26",
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));
@@ -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;
@@ -628,7 +638,7 @@ highp float composeHeight(vec3 dir0, highp vec2 faceLocal, float tileM){ // W7
628
638
  // anchorpoints (low reliefMul = flat plains, non-mtn, non-wet, non-cold). Fades to zero
629
639
  // by reliefMul ~0.5 so mountains are unaffected.
630
640
  float flatGate = max(0.0, 1.0 - reliefMul * 2.0);
631
- h += snoise3(dir0 * 1200.0) * flatGate * uDetailOverlay * 20.0;
641
+ h += snoise3(dir0 * 1400.0) * flatGate * uDetailOverlay * 35.0;
632
642
  // LAKE CARVE + flat-water plane
633
643
  float lakeWetV; float lakeCarveRaw = lakeCarveM(dir0, lakeWetV);
634
644
  // uCarveWide=1 widens the carve CLIMATE gates so the gorge/lake/dune depth fades in over a wide
@@ -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));
@@ -1860,7 +1871,7 @@ void main() {
1860
1871
  // no extra sampling, seam-safe, zero FPS cost vs the old single-term version.
1861
1872
  float aoAmt = (uAoAmt > 0.0 ? uAoAmt : 1.0);
1862
1873
  float gorgeAO = 0.0; // canyon AO decoupled (user 2026-06-10: canyons impose ELEVATION only, no material/AO keying)
1863
- float slopeAO = smoothstep(0.45, 0.95, slope) * 0.40; // steep concave faces see less sky (0.30->0.40, deeper valleys)
1874
+ float slopeAO = smoothstep(0.20, 0.90, slope) * 0.35; // gentle-slope AO reveals midday relief (0.20->0.90 from 0.45->0.95, peak 0.35)
1864
1875
  float cliffAO = 1.0 - min(gorgeAO + slopeAO, 0.75) * aoAmt; // cap so faces never go black
1865
1876
  // STRONGER NORMAL-DRIVEN SHADING (user 2026-06-03: 'normals arent affecting the lit view properly').
1866
1877
  // The lit relief was too subtle (witnessed litON SD 6.4 vs flat 5.9 = only ~8% contrast) because
@@ -1883,7 +1894,7 @@ void main() {
1883
1894
  // W5: vShadeAO (per-vertex, from the deleted VS gradient) RECOMPUTED here from the Sobel slope (user
1884
1895
  // 2026-06-07 'recompute AO from Sobel'). creaseAO = slope*0.45 (steep landform -> valley/crease
1885
1896
  // occlusion); microAO from the fine per-pixel gslope. uVertexAO lever + the 0.45 floor preserved.
1886
- float fsShadeAO = clamp(1.0 - (slope * 0.45 + gslope * 0.35) * uVertexAO, 0.45, 1.0);
1897
+ float fsShadeAO = clamp(1.0 - (slope * 0.55 + gslope * 0.40) * uVertexAO, 0.50, 1.0);
1887
1898
  // FADE-IN FIX (user 'a layer fades in at close distance'): vAO was mix(1.0, fsShadeAO, nearFade) where
1888
1899
  // nearFade rises 0->1 as pxWorld shrinks 180->8m on approach -> the Sobel crease-AO darkening animated
1889
1900
  // IN as the camera neared = the visible 'layer fading in'. fsShadeAO is a per-pixel SOBEL-slope quantity
@@ -2056,7 +2067,7 @@ void main() {
2056
2067
  // unaffected because the fill is weighted by (1-dayShade). Raised from the old earthshine 0.012/0.018/
2057
2068
  // 0.032 (still read as black) to a clearly-visible dim blue so framed night terrain stays legible.
2058
2069
  vec3 nightFill = vec3(0.06, 0.075, 0.11) * uNightLights;
2059
- vec3 color2 = (color * dayShade + nightFill * (1.0 - dayShade)) * limb;
2070
+ vec3 color2 = (color * dayShade + nightFill * (1.0 - dayShade));
2060
2071
  // HDR -> SDR: ACES tonemap. Exposure 1.7 -> 1.25 so the N.sun + dayShade gradient SPREADS across the
2061
2072
  // tonemap's linear range instead of clipping flat to the bright shoulder (the orbit-flatness root).
2062
2073
  vec3 c = color2 * uExposure; // exposure 1.25->uExposure(1.0): stop the bright ACES-shoulder wash