mapspinner 0.1.59 → 0.1.61

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.59",
3
+ "version": "0.1.61",
4
4
  "description": "WebGL2 Earth-scale terrain rendering SDK for interactive globe applications",
5
5
  "main": "src/index.js",
6
6
  "exports": {
@@ -1237,7 +1237,11 @@ uniform vec4 uSurfMeanL; // per-layer mean linear luminance of the photo col
1237
1237
  // binary boundary snaps material across wide areas instead of softly interfingering, and the high-freq
1238
1238
  // octave aliased on bright materials. Reverted to the clean smoothstep boundaries; a non-aliasing
1239
1239
  // 'interesting boundary' technique (e.g. a wide soft transition material band) is a separate future task.
1240
- vec3 terrainAlbedo(float h, float slope, float rockSlope, highp vec3 worldPos) { // highp: worldPos feeds normalize(worldPos)*freq noise UVs -- mediump would scramble the lattice at close range
1240
+ vec3 terrainAlbedo(float h, float slope, float rockSlope, highp vec3 worldPos, float pxW) { // highp: worldPos feeds normalize(worldPos)*freq noise UVs -- mediump would scramble the lattice at close range
1241
+ // DISTANCE-WIDENED rock-slope band (user 2026-06-14 'hard edge to rock slopes at a distance'): the
1242
+ // close-up height-blend doesn't run far off, so the macro slope gate showed a hard edge. Widen its
1243
+ // upper threshold with distance so the rock->grass slope boundary fades softly when it's far.
1244
+ float rockWiden = smoothstep(20.0, 500.0, pxW) * 0.20;
1241
1245
  vec3 c;
1242
1246
  if (h < 0.0) {
1243
1247
  // SEABED CONTINUES AS LAND MATERIAL (user 2026-06-11 'instead of turning terrain into water,
@@ -1258,7 +1262,7 @@ vec3 terrainAlbedo(float h, float slope, float rockSlope, highp vec3 worldPos) {
1258
1262
  float bandWarp = (snoise3(bww * 210.0) * 1.0 + snoise3(bww * 560.0) * 0.5 + snoise3(bww * 1450.0) * 0.25) * 720.0;
1259
1263
  c = mix(c, bcRock, smoothstep(bandEdgesHi.x + bandWarp, bandEdgesHi.y + bandWarp, h));
1260
1264
  c = mix(c, bcSnow, smoothstep(snowEdges.x + bandWarp, snowEdges.y + bandWarp, h));
1261
- c = mix(c, bcRock, smoothstep(slopeRock.x, slopeRock.y, rockSlope) * step(0.0, h));
1265
+ c = mix(c, bcRock, smoothstep(slopeRock.x, slopeRock.y + rockWiden, rockSlope) * step(0.0, h));
1262
1266
  // OLD PROCEDURAL GREY ROCKFACE DELETED (max-speed sweep 2026-06-10, user 'replace the original
1263
1267
  // rock completely'): the photo-rock splat owns steep faces; the 3-tap grey fBm fallback is gone.
1264
1268
  }
@@ -1300,7 +1304,7 @@ vec3 biomeColor(float temp, float humid) {
1300
1304
  // FOREST vs MEADOW split (user: they must look distinct, not one green). A CRISPER humidity
1301
1305
  // boundary (0.48->0.56, was 0.46->0.66) so meadow (drier-temperate) and forest (wetter) read
1302
1306
  // as DISTINCT adjacent regions instead of a long ambiguous blend; very-wet deepens to canopy.
1303
- float wet = smoothstep(0.48, 0.56, humid);
1307
+ float wet = smoothstep(0.40, 0.66, humid); // WIDENED 0.48-0.56 -> 0.40-0.66 (user 2026-06-14 'hard lines between light and dark grass'): soft meadow<->forest blend, not a crisp line
1304
1308
  float veryWet = smoothstep(0.62, 0.80, humid);
1305
1309
  vec3 c = MEADOW; // temperate mid-humidity default
1306
1310
  c = mix(c, FOREST, wet); // wet -> forest (crisp boundary)
@@ -1391,7 +1395,7 @@ float canyonMask(vec3 worldPos, float h, float temp, float humid, float px, out
1391
1395
  #endif // biomeClassColor/riverMask/canyonMask: DEBUGVIEW-only (called solely from displayMode blocks) -- excluded from render FS cold-compile (FS-2, workflow w4y1bnrqc)
1392
1396
 
1393
1397
  vec3 terrainAlbedoClimate(float h, float slope, float rockSlope, float temp, float humid, highp vec3 worldPos, float pxWorld) { // highp: worldPos feeds normalize(worldPos)*freq noise UVs (mottle/river/canyon ridge) -- mediump scrambles the lattice up close
1394
- vec3 c = terrainAlbedo(h, slope, rockSlope, worldPos);
1398
+ vec3 c = terrainAlbedo(h, slope, rockSlope, worldPos, pxWorld);
1395
1399
  if (h < 0.0) {
1396
1400
  // SEA ICE: near-polar ocean (very cold) freezes to white-blue pack ice. Pure fn of the
1397
1401
  // anchor temp -> seam-safe; the soft threshold gives an irregular (not hard-zonal) margin.
@@ -1872,63 +1876,46 @@ void main() {
1872
1876
  // samples through this shared wt, so the warp cannot layer or double-apply.
1873
1877
  wt += vTexWarp * uTexWarp;
1874
1878
  vec3 tw = abs(n); tw = tw * tw; tw /= (tw.x + tw.y + tw.z + 1e-4);
1875
- vec4 albA = surfTriTap(uSurfAlb, wt, tw, lA);
1876
- vec3 nrmA = surfTriNrm(uSurfNrm, wt, tw, lA, n);
1877
- float bAB = clamp(wA / max(wA + wB, 1e-4), 0.0, 1.0);
1878
- vec4 texAlb = albA; vec3 texNrm = nrmA;
1879
- if (wB > 0.02) { // second layer only where a real transition exists (saves 6 taps elsewhere)
1880
- vec4 albB = surfTriTap(uSurfAlb, wt, tw, lB);
1881
- vec3 nrmB = surfTriNrm(uSurfNrm, wt, tw, lB, n);
1882
- // displacement-sharpened transition -- WEIGHT-DOMINANT (user 2026-06-10 'large patches
1883
- // of rock texture in mountains, not slope-keyed'): the old (hA-hB)*4 let the texture's
1884
- // 2.4km displacement features decide the material outright wherever the gate weights sat
1885
- // mid-range, so whole displacement blobs flipped to rock. The slope/snow/climate weight
1886
- // now dominates (x3) and displacement only crisps the edge (x0.8) within the true
1887
- // transition band -- material placement is the gates', texture only shapes the seam.
1888
- // displacement term 0.8 -> 0.3 (user 2026-06-11 'still see bowls of rock texture'):
1889
- // 0.8 still let the displacement photo's bowl-shaped blobs flip whole patches to rock
1890
- // inside the transition band; 0.3 only feathers the seam edge.
1891
- // SOFTENED (2026-06-13): transition sharpening reduced 3->1 so grass/rock/sand/snow
1892
- // boundaries hold a natural blend band instead of a hard die-cut line. The 1.0 slope
1893
- // still prefers the dominant layer but leaves a visible transition zone.
1894
- // DISPLACEMENT-DRIVEN GRADIENT for ALL material pairs (user 2026-06-14 'all textures use the
1895
- // displacement to mix the gradient'): height-blend the top-2 layers by their displacement so
1896
- // every boundary (grass/rock, grass/sand, rock/snow...) follows the texture RELIEF = irregular,
1897
- // not a straight gate line. The displacement weight ramps UP close (0.3 -> 1.3 by the deck)
1898
- // where the hard lines show, and stays low far off so the 2.4km photo's bowl features never
1899
- // flip whole patches to rock (the documented bowl-blob lesson -- that was a DISTANT artifact).
1900
- // UNIVERSAL displacement-driven blend (user 2026-06-14 'this must be ALL texture blends -- the
1901
- // rock slope and the biome divisions for height and area, wherever we blend textures'): this
1902
- // bSharp is the ONE blend between the top-2 splat layers, so it already covers every material
1903
- // pair. WEAKEN the weight term (1.0->0.45) and STRENGTHEN the displacement (-> up to 1.6 close)
1904
- // so the texture RELIEF -- not the gate weight -- decides the local winner over a WIDE weight
1905
- // band: every boundary (slope-rock, height-snow, climate/area-biome, beach) fingers along the
1906
- // texture bumps. Displacement ramps DOWN far off so the 2.4km bowls never flip distant patches.
1907
- float dispW = mix(0.45, 1.6, 1.0 - smoothstep(3.0, 80.0, pxWorld));
1908
- float bSharp = clamp((bAB * 2.0 - 1.0) * 0.45 + (albA.a - albB.a) * dispW + 0.5, 0.0, 1.0);
1909
- texAlb = mix(albB, albA, bSharp);
1910
- texNrm = mix(nrmB, nrmA, bSharp);
1911
- }
1912
- // FINE DETAIL OCTAVE (user 2026-06-14 'add one octave at 4x smaller -> world better scaled at
1913
- // 2m'): the base photo tiles at uTexTileM (~2.4km = ~2.3m/texel, smeared at the deck). Sample the
1914
- // SAME dominant material at 4x frequency (~0.6m/texel) and overlay its luminance + normal so
1915
- // close-up the ground gains sub-metre structure. Faded out by ~12m px so it never moires far off.
1916
- // LOW-FREQ = NORMALS ONLY, HIGH-FREQ = TEXTURE + STRONG NORMALS (user 2026-06-14): flatten the
1917
- // base (2.4km) albedo to its luminance so the MACRO biome color carries the chroma -- the base
1918
- // octave contributes only its NORMAL (relief), not color blotches. The 4x detail octave then
1919
- // provides the albedo STRUCTURE + a strong normal.
1920
- texAlb.rgb = vec3(dot(texAlb.rgb, vec3(0.299, 0.587, 0.114))); // low-freq: normals only (albedo -> flat luma)
1879
+ const vec3 LUMA = vec3(0.299, 0.587, 0.114);
1921
1880
  float detailFade = (1.0 - smoothstep(1.0, 12.0, pxWorld));
1881
+ float bAB = clamp(wA / max(wA + wB, 1e-4), 0.0, 1.0);
1882
+ // PER-LAYER material = base octave (NORMALS ONLY -> albedo flattened to luma, macro color carries
1883
+ // chroma) + the 4x DETAIL octave (albedo STRUCTURE + strong normal) + a per-layer DISPLACEMENT
1884
+ // (coarse base, refined by the 4x near the deck). Each top-2 layer is built FULLY here, then the
1885
+ // two are HEIGHT-BLENDED (below) -- so the detail never flips at the boundary (the 'hard lines up
1886
+ // close ever since the higher octave' bug = the detail used the dominant layer only).
1887
+ vec4 albA = surfTriTap(uSurfAlb, wt, tw, lA);
1888
+ vec3 cA = vec3(dot(albA.rgb, LUMA)); vec3 nA = surfTriNrm(uSurfNrm, wt, tw, lA, n); float dispA = albA.a;
1922
1889
  if (detailFade > 0.01) {
1923
1890
  vec4 dA = surfTriTap(uSurfAlb, wt * 4.0, tw, lA);
1924
- float dl = dot(dA.rgb, vec3(0.299, 0.587, 0.114));
1925
- float bl = dot(texAlb.rgb, vec3(0.299, 0.587, 0.114));
1926
- texAlb.rgb *= mix(1.0, clamp(dl / max(bl, 0.04), 0.35, 2.4), detailFade * 1.3); // HIGH-FREQ albedo structure (heavier)
1927
- // detail NORMAL: surfTriNrm returns a PERTURBATION vector (texNrm feeds texDn = texNrm*uTexNrmK*k,
1928
- // NOT a unit normal), so the detail is simply ADDED in the same space -- no normalize (that
1929
- // scrambled magnitudes = the 'weird highest-octave normals').
1930
- vec3 dN = surfTriNrm(uSurfNrm, wt * 4.0, tw, lA, n);
1931
- texNrm = texNrm + dN * detailFade * 1.4; // BIGGER normal effect for the high-freq octave (user 2026-06-14)
1891
+ cA *= mix(1.0, clamp(dot(dA.rgb, LUMA) / max(dot(cA, LUMA), 0.04), 0.35, 2.4), detailFade * 1.3);
1892
+ nA += surfTriNrm(uSurfNrm, wt * 4.0, tw, lA, n) * detailFade * 1.4;
1893
+ dispA = mix(dispA, dA.a, detailFade); // fine displacement poke near the deck
1894
+ }
1895
+ vec4 texAlb = vec4(cA, dispA); vec3 texNrm = nA;
1896
+ if (wB > 0.02) { // second layer only where a real transition exists
1897
+ vec4 albB = surfTriTap(uSurfAlb, wt, tw, lB);
1898
+ vec3 cB = vec3(dot(albB.rgb, LUMA)); vec3 nB = surfTriNrm(uSurfNrm, wt, tw, lB, n); float dispB = albB.a;
1899
+ if (detailFade > 0.01) {
1900
+ vec4 dB = surfTriTap(uSurfAlb, wt * 4.0, tw, lB);
1901
+ cB *= mix(1.0, clamp(dot(dB.rgb, LUMA) / max(dot(cB, LUMA), 0.04), 0.35, 2.4), detailFade * 1.3);
1902
+ nB += surfTriNrm(uSurfNrm, wt * 4.0, tw, lB, n) * detailFade * 1.4;
1903
+ dispB = mix(dispB, dB.a, detailFade);
1904
+ }
1905
+ // HEIGHT-BLEND POKE-THROUGH (user 2026-06-14 'each texture's higher areas should poke through
1906
+ // the other, offset by the ramp'): each layer's height = its DISPLACEMENT + a weight-ramp
1907
+ // offset (so the gate still positions the boundary). The higher height wins, blended over a
1908
+ // soft WIDTH so the loser's high bumps still poke through near the ramp -> interlocking
1909
+ // fingers, never a hard line. This is the ONE blend for ALL pairs (slope-rock, height-snow,
1910
+ // climate/area-biome, beach). bw widens close-up (where the detail is rich) for a softer mesh.
1911
+ float bw = mix(0.12, 0.30, detailFade);
1912
+ float hA = dispA + (bAB - 0.5) * 1.1;
1913
+ float hB = dispB + (0.5 - bAB) * 1.1;
1914
+ float mh = max(hA, hB) - bw;
1915
+ float waH = max(hA - mh, 0.0), wbH = max(hB - mh, 0.0);
1916
+ float bSharp = waH / max(waH + wbH, 1e-4);
1917
+ texAlb = vec4(mix(cB, cA, bSharp), mix(dispB, dispA, bSharp));
1918
+ texNrm = mix(nB, nA, bSharp);
1932
1919
  }
1933
1920
  float k = uTexMix * texFarFade;
1934
1921
  // macro-tinted detail (user 2026-06-10 'the textured patch must be tinted to the same shade
@@ -1957,7 +1944,10 @@ void main() {
1957
1944
  // ground reads unambiguously as its material (grass green / sand tan / rock grey) without the
1958
1945
  // raw photo's darkness ('terrain gets darker' lesson) and without the macro's biome-brown
1959
1946
  // repaint ('neither grass nor sand' defect). Layer-mean normalized like `detail`.
1960
- vec3 texIdent = texC * (dot(albedo, vec3(0.2126, 0.7152, 0.0722)) / max(texL, 0.02));
1947
+ // low-freq albedo is now NORMALS-ONLY (texC is luma structure), so the old raw-photo 'identity'
1948
+ // hue is gone; texIdent == detail = MACRO color * structure (keeps rock its macro tan-grey, not
1949
+ // grey). The 4x detail carries the fine structure for every material.
1950
+ vec3 texIdent = texC * (albedo / max(texL, 0.02));
1961
1951
  albedo = clamp(mix(albedo, mix(detail, texIdent, photoF), k), 0.0, 1.0);
1962
1952
  // displacement-normal relief: WORLD-SPACE UDN perturbation from surfTriNrm (each projection
1963
1953
  // plane's tangent axes, not the radial frame). Amplitude capped low (scramble lesson d262b5e);