mapspinner 0.1.4 → 0.1.5

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/AGENTS.md CHANGED
@@ -33,7 +33,7 @@ camera you can't aim. Use the DATA diagnostics — `__diag.pxPerPoly()` (on-scre
33
33
  ## The terrain pipeline in one page (read this before touching terrain)
34
34
 
35
35
  Earth-scale terrain SDK, WebGL2, served at `http://localhost:8080/` (entry `planet.html`, `server.js`).
36
- GPU one-fractal: no Proland tile producer, no cascade. A finer
36
+ GPU one-fractal: no tile producer. A finer
37
37
  LOD is a denser sample of the SAME field. The procedural broadShapeM fractal is the LIVE render
38
38
  path (hasAtlas==0, the default). The baked atlas is opt-in (planet-orchestrator.js opts.atlas===true;
39
39
  live `window.__toggleAtlas(on)` / `__forceAtlas`); its bake is CLI-validated faithful (atlas-bake.mjs
@@ -58,7 +58,7 @@ probes, and `window.__diag.reloadShaders()` hot-reload through `/cmd` — no pag
58
58
 
59
59
  Data flow, each stage names its one file:
60
60
  1. QUADTREE picks which cube-sphere patches to draw per camera altitude — `src/quadtree.js`
61
- (cube-sphere quadtree in JS, ported from the deleted Proland C++), driven per frame by
61
+ (cube-sphere quadtree in JS), driven per frame by
62
62
  `src/planet-orchestrator.js`.
63
63
  2. MESH per patch is a GRID+2 grid (`src/gl-render.js`) whose outer ring is a SKIRT (terrain.glsl
64
64
  drops `vertex.z>0.5` verts radially below the surface) hiding LOD T-junction cracks. The outer
@@ -70,7 +70,7 @@ Data flow, each stage names its one file:
70
70
  `inciseRidgeField`. Collision = a GPU `_PROBE_` variant of the SAME shader (1px readback,
71
71
  `gl-render.sampleGroundM`), no CPU mirror. — full design: recall "TV8 GPU-TERRAIN ARCHITECTURE
72
72
  DECISION" in rs-learn.
73
- 4. DEFORM: direct per-vertex sphere projection — `vWorld = dir0 * (R + h)` (replaced the Proland
73
+ 4. DEFORM: direct per-vertex sphere projection — `vWorld = dir0 * (R + h)`
74
74
  corner-blend deform; round at any tessellation, no flat patches at high GRID).
75
75
  5. FS shades from height+slope+climate (`terrainAlbedoClimate`) + per-vertex seamless normal +
76
76
  ocean/lake/river. No FS detail TEXTURE (a tiled image would moire + UV-scroll); closeup MACRO
package/CHANGELOG.md CHANGED
@@ -24,7 +24,7 @@
24
24
 
25
25
  ## 2026-05-23
26
26
 
27
- - `terrain-phase2.js` (commit `d87c51b`): Add `allocateTileSlot`, `computePipelines`, `computeBindGroups` to `ProlandProducer` — stopped TypeError crash every render frame when `terrain-phase3-integration.js` called these missing methods.
27
+ - `terrain-phase2.js` (commit `d87c51b`): Add `allocateTileSlot`, `computePipelines`, `computeBindGroups` — stopped TypeError crash every render frame when `terrain-phase3-integration.js` called these missing methods.
28
28
  - `shader-loader.js` (commit `1e61b7b` session): Fix `#if` numeric literal handling — `#if 1` was treated as a flag lookup (always false), stripping always-true blocks and causing GPU validation errors and black canvas.
29
29
  - `terrain-phase1.js`: Change `normTexArray` format from `rg32float` to `rgba8unorm` to match `normal_producer.wgsl` storageTexture output.
30
30
  - `terrain-phase2.js`: Remove unused bind group entries for upsample (b5), normal producer (b0), ortho producer (b0,b1,b2,b6) — WebGPU `layout:'auto'` strips unreachable bindings, causing validation errors when they were supplied.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapspinner",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "WebGL2 Earth-scale terrain rendering SDK for interactive globe applications",
5
5
  "main": "src/index.js",
6
6
  "exports": {
package/planet.html CHANGED
@@ -1,5 +1,5 @@
1
1
  <!doctype html>
2
- <html><head><meta charset="utf-8"><title>TerrainView8 -- authentic Proland planet (WebGL2)</title>
2
+ <html><head><meta charset="utf-8"><title>mapspinner WebGL2 Terrain SDK</title>
3
3
  <style>
4
4
  html,body{margin:0;height:100%;background:#000;color:#9f9;font:12px monospace;overflow:hidden}
5
5
  canvas{display:block;width:100vw;height:100vh}
@@ -14,9 +14,9 @@
14
14
  #panel .grp{border-top:1px solid #3f3;padding-top:4px;margin-top:6px;color:#cfc}
15
15
  </style></head>
16
16
  <body>
17
- <!-- The authentic Proland WebGL2 path (proland-planet-orchestrator) renders here. -->
17
+ <!-- The mapspinner WebGL2 renderer renders here. -->
18
18
  <canvas id="cwebgl" style="position:fixed;inset:0;width:100vw;height:100vh"></canvas>
19
- <div id="hud"><b>TerrainView8 (authentic Proland, WebGL2)</b><br>
19
+ <div id="hud"><b>mapspinner WebGL2 Terrain SDK</b><br>
20
20
  Controls: <b>WASD</b>/drag orbit . <b>Q/E</b>/wheel zoom . <b>R</b> reset<br>
21
21
  <span id="stats">init...</span></div>
22
22
  <div id="panel">
@@ -33,18 +33,16 @@ Controls: <b>WASD</b>/drag orbit . <b>Q/E</b>/wheel zoom . <b>R</b> reset<br>
33
33
  <div id="genPanel" style="position:fixed;left:8px;top:34px;z-index:30;display:none;max-height:88vh;overflow:auto;background:rgba(12,16,20,0.92);color:#cfe;font:10px/1.5 monospace;padding:6px 8px;border:1px solid #2a3a44;width:250px"></div>
34
34
  <div id="err"></div>
35
35
  <script type="module">
36
- // [P2-6 follow-up] The authentic Proland terrain + atmosphere + ocean run entirely
37
- // on the WebGL2 path (proland-planet-orchestrator, loaded lazily in frameLoop). The
38
- // legacy WebGPU device/pipelines/ocean/atmosphere + the JS quadtree + our CPU
39
- // proland_terrain.wasm (which only drove that dead path) have been DELETED.
36
+ // The mapspinner terrain + atmosphere + ocean run entirely
37
+ // on the WebGL2 path, loaded lazily in frameLoop. The
38
+ // legacy WebGPU device/pipelines/ocean/atmosphere have been DELETED.
40
39
  const errEl=document.getElementById('err');
41
40
  const showErr=m=>{errEl.style.display='block';errEl.textContent=String(m);window.__pageErr=String(m);console.error(m)};
42
41
  window.addEventListener('error',e=>showErr(e.message||e));
43
42
  window.addEventListener('unhandledrejection',e=>showErr(e.reason?.message||e.reason||e));
44
43
 
45
- // [P2-6 follow-up] The legacy WebGPU render path has been DELETED. The authentic
46
- // Proland terrain + atmosphere + ocean all run on the WebGL2 path
47
- // (proland-planet-orchestrator) drawing to #cwebgl. `canvas` is the WebGL2 canvas;
44
+ // The legacy WebGPU render path has been DELETED. All terrain + atmosphere + ocean
45
+ // run on the WebGL2 path drawing to #cwebgl. `canvas` is the WebGL2 canvas;
48
46
  // camera event handlers + sizing read it.
49
47
  const canvas=document.getElementById('cwebgl');
50
48
 
@@ -124,8 +122,7 @@ const normalize=v=>{const r=Math.hypot(...v)||1;return [v[0]/r,v[1]/r,v[2]/r]};
124
122
  // where the camera starts (cam.pos below), not by this scalar -- RADIUS just
125
123
  // sets the units everything else is denominated in.
126
124
  const RADIUS=6371.0;
127
- // WebGL2 Proland overlay works in METERS (its producer generates meter-scale
128
- // elevation). Surface radius 6.36e6 m; the km-unit world maps to it 1 unit = this many m.
125
+ // WebGL2 terrain works in METERS. Surface radius 6.36e6 m; the km-unit world maps to it 1 unit = this many m.
129
126
  const WEBGL2_TERRAIN_R_M = 6360000.0;
130
127
  const WEBGL2_M_PER_UNIT = WEBGL2_TERRAIN_R_M / RADIUS;
131
128
  // EXPOSE the world-scale constants as globals so a scripted browser-witness (page.evaluate runs
@@ -298,8 +295,7 @@ async function pollCmd() {
298
295
  } catch (_) {} finally { __cmdBusy = false; }
299
296
  }
300
297
  setInterval(pollCmd, 500);
301
- // P2-6: the authentic Proland WebGL2 terrain (proland-planet-orchestrator) is the
302
- // renderer. Set window.__useWebGL2Terrain=false to disable (no fallback now).
298
+ // The mapspinner WebGL2 terrain is the renderer.
303
299
  if (window.__useWebGL2Terrain === undefined) window.__useWebGL2Terrain = true;
304
300
  const stats=document.getElementById('stats');
305
301
  // SINGLE-FLIGHT FRAME SCHEDULER (2026-06-12): every frameLoop continuation routes through here so
@@ -314,8 +310,7 @@ function frameLoop(){
314
310
  const frameMs = now - lastT; // true inter-frame interval (for FPS)
315
311
  const dt=Math.min(0.05,(now-lastT)/1000); lastT=now;
316
312
 
317
- // ---- Authentic-Proland WebGL2 terrain path. Lazily inits the compiled Proland
318
- // wasm + thin WebGL2 renderer (proland-planet-orchestrator) on the #cwebgl canvas.
313
+ // ---- mapspinner WebGL2 terrain path. Lazily inits the renderer on the #cwebgl canvas.
319
314
  // The actual render + rAF happens AFTER the camera-update section below (so WASD/
320
315
  // drag/zoom + autopilot move the camera before the frame is drawn).
321
316
  if (window.__useWebGL2Terrain) {
@@ -546,7 +541,7 @@ function frameLoop(){
546
541
  window.__altM = heightAboveGroundM;
547
542
 
548
543
  // The camera (autopilot + free-fly + clamp) has now been fully updated this frame,
549
- // so render the authentic Proland WebGL2 planet with the CURRENT camera. This is
544
+ // so render the planet with the CURRENT camera. This is
550
545
  // the sole render path; it advances the sun, draws via the orchestrator, updates
551
546
  // the HUD/title/diag, schedules the next frame, and returns.
552
547
  if (window.__useWebGL2Terrain && window.__planetOrch) {
@@ -705,8 +700,8 @@ cam.oceanAmplitude = 1.0 * (0.4 + 5 / 12.0); // == old default (windSpeed 5)
705
700
  cam.oceanChoppiness = 1.6 * (0.5 + 0.5); // == old default (seaState 0.5)
706
701
  cam.oceanFoam = 0.5;
707
702
  cam.oceanTimeScale = 1.0;
708
- // Display-mode toggle (tv-display-modes-toggle). Skirts removed: LOD cracks are now hidden
709
- // by the one-cell overlap ring in the mesh (proland-gl-render), not a dropped skirt curtain.
703
+ // Display-mode toggle. Skirts removed: LOD cracks are now hidden
704
+ // by the one-cell overlap ring in the mesh, not a dropped skirt curtain.
710
705
  // User picking from the dropdown is an explicit action that must WIN over any stale diagnostic
711
706
  // override (window.__displayMode left set by __dbg.setDisplayMode/census/aimDir). Clear the override
712
707
  // so the menu value takes effect immediately -- otherwise the precedence pick in the render loop
@@ -12,10 +12,9 @@
12
12
  // only that cell's neighbourhood -- never the whole planet.
13
13
  //
14
14
  // INTEGRATION: this is a pure-JS field (editable, serialisable). The orchestrator samples
15
- // it per Proland tile and pushes the result into the wasm via the existing setters + a
16
- // per-tile additive bias, so Proland's upsample-coherence and seamlessness are preserved
17
- // (the field is C0-continuous across tile edges because every tile samples the SAME global
18
- // quadtree partition at its own scale band). No wasm rebuild is needed to edit params.
15
+ // it per tile and pushes the result into the terrain VS via a uniform or texture.
16
+ // The field is C0-continuous across tile edges because every tile samples the SAME global
17
+ // quadtree partition at its own scale band.
19
18
  //
20
19
  // PERFORMANCE (all the most-performant choices):
21
20
  // - Param storage is a flat Float32Array per band (cache-friendly, zero object churn),
@@ -26,7 +25,7 @@
26
25
  // - Sampling is O(bands) -- a handful of band lookups + one bilinear per band -- with no
27
26
  // allocation on the hot path (results written into a reused scratch object).
28
27
 
29
- // ---- cube-face frame (mirrors proland-planet-orchestrator FACE_FRAME / render localToWorld3).
28
+ // ---- cube-face frame (mirrors planet-orchestrator FACE_FRAME / render localToWorld3).
30
29
  // A face-local point (u,v in [-1,1], outward axis) maps to a world direction. We only need
31
30
  // the inverse (world dir -> face + uv) for sampleDir, and the forward (face,uv -> dir) for
32
31
  // the per-node procedural hash seed coordinate.
@@ -415,7 +414,7 @@ export function createAnchorField(opts = {}) {
415
414
  return _scratch;
416
415
  }
417
416
 
418
- // PER-TILE sample for the orchestrator: a Proland tile (face, level, tx, ty) covers a uv
417
+ // PER-TILE sample for the orchestrator: a tile (face, level, tx, ty) covers a uv
419
418
  // square on its face; sample the field at the tile CENTRE (the bias is per-tile, and the
420
419
  // field is C0 across tiles because every tile samples the same global band quadtrees).
421
420
  function sampleTile(face, level, tx, ty) {
package/src/gl-render.js CHANGED
@@ -561,7 +561,7 @@ export async function initMapspinnerRender(gl, opts = {}) {
561
561
  const instBuf=gl.createBuffer(); // per-instance [ox,oy,l,level,face] (filled per frame in render())
562
562
 
563
563
  // per-face local->world (cube face -> sphere local frame). Column-major mat3 packed
564
- // into a Float32Array(9). Matches prolandProducers.buildLocalToWorld convention:
564
+ // into a Float32Array(9). Matches localToWorld3 convention:
565
565
  // col0 = U/rs, col1 = faceCenter, col2 = V/rs. rootQuadSize=2 -> face spans [-1,1].
566
566
  function localToWorld3(face) {
567
567
  // face axes (cube): for face 3 (+Z) U=+X, V=+Y, center=+Z. Generic table:
@@ -202,7 +202,7 @@ export async function initMapspinnerPlanet(gl, opts = {}) {
202
202
 
203
203
  // ---- HIERARCHICAL PARAMETER FIELD (HPF) ----------------------------------------
204
204
  // The anchor field drives generation as a CONTINUOUS function of world direction. To keep
205
- // Proland upsample-coherence + seamlessness we feed it to the terrain VS as a TEXTURE (a
205
+ // For seamlessness we feed it to the terrain VS as a TEXTURE (a
206
206
  // continuous C0 field via LINEAR filtering) rather than a per-tile constant (a per-tile
207
207
  // step would reintroduce seams -- see the CLOD mean-drift / edge-equality lessons). We bake
208
208
  // the field's continental band into a per-face HPF_RES^2 RGBA32F 2D-array (seaBias, elevAmp,
@@ -414,7 +414,7 @@ export async function initMapspinnerPlanet(gl, opts = {}) {
414
414
  fallbackCount: c.fallbackCount, maxFallbackLevel: c.maxFallbackLevel, frontFallback: c.frontFallback, cached: true };
415
415
  }
416
416
 
417
- // configure the authentic Proland quadtree once (meter units; root l=2R).
417
+ // configure the quadtree once (meter units; root l=2R).
418
418
  // splitFactor is LIVE-TUNABLE via window.__splitFactor so the px/poly target
419
419
  // (4-50 px per triangle edge) can be calibrated in-browser with one dispatch
420
420
  // (no wasm rebuild / reload) -- the efficiency analog of __diag.setGen. A SMALLER
package/src/quadtree.js CHANGED
@@ -1,7 +1,4 @@
1
- // quadtree.js -- the cube-face terrain quadtree LOD selection, ported from the deleted
2
- // proland_terrain.cpp (TerrainNode/TerrainQuad subdivision + SphericalDeformation + the
3
- // pixel-calibrated split distance). This is the ONE piece of the old C++/wasm the render
4
- // needed; everything else (the elevation/normal/ortho atlas producer + cascade) is gone --
1
+ // quadtree.js -- the cube-face terrain quadtree LOD selection.
5
2
  // terrain shape is the single GPU fractal (broadShapeM in terrain.glsl), evaluated per-vertex.
6
3
  //
7
4
  // Pure JS, no wasm, no allocation per frame beyond the leaf array. One Quadtree instance per
@@ -1,6 +1,6 @@
1
1
  // src/shaders/atmosphere.glsl (#version 300 es is prepended by the JS loader)
2
2
  // -----------------------------------------------------------------------------
3
- // WebGL2 analytic Rayleigh/Mie single-scatter atmosphere for the Proland planet.
3
+ // WebGL2 analytic Rayleigh/Mie single-scatter atmosphere for the planet.
4
4
  //
5
5
  // PORT NOTE — what this is vs. the WebGPU path:
6
6
  // planet.html runs the AUTHORITATIVE Bruneton model: it BAKES the transmittance/
@@ -1,14 +1,9 @@
1
- // Proland authentic terrain render shader, ported to WebGL2 (#version 300 es) from
2
- // vendor/proland-build/proland-git/terrain/examples/terrain2/terrainShader.glsl.
3
- // VS: spherical deformation + CLOD blend (zf<->zc) of the wasm-driven elevation tiles.
4
- // FS: sample normal + (placeholder) albedo, simple sun-lit Lambert with an ambient floor.
5
- // Driven by the thin WebGL2 render layer (per-quad deformation uniforms + textureTile
6
- // coords from the Proland wasm). No geometry/tess -> WebGL2-clean. The JS prepends
7
- // #version + precision and compiles _VERTEX_ / _FRAGMENT_ separately.
1
+ // mapspinner WebGL2 terrain render shader.
2
+ // VS: spherical deformation + direct per-vertex sphere projection.
3
+ // FS: sample normal + albedo, sun-lit Lambert with ambient floor.
4
+ // The JS prepends #version + precision and compiles _VERTEX_ / _FRAGMENT_ separately.
8
5
 
9
- // (Proland SamplerTile/textureTile atlas plumbing DELETED 2026-06-11 dead-code sweep: never set, never called.)
10
-
11
- // --- DIRECT per-vertex sphere-projection uniforms (replaces the Proland corner-blend CLOD) ------
6
+ // --- DIRECT per-vertex sphere-projection uniforms ------
12
7
  // SINGLE INSTANCED DRAW: defOffset (ox,oy,l,level) + the face frame are PER-INSTANCE now (one
13
8
  // gl.drawElementsInstanced over the whole visible leaf set, no per-quad uniform churn). In the VS
14
9
  // they come from instance attributes (iOffset + iFace); defRadius/defViewProjRel stay uniforms
@@ -712,7 +707,7 @@ mat3 faceFrame(float f){
712
707
  // function of world dir -> replaces the old hardcoded sin/cos lobe with the editable field.
713
708
  highp float continentalBias(vec3 dir) { return hpfSample(dir).r; } // W7: R = seaBias (meters) -> highp
714
709
 
715
- // ---- CONTINUOUS BROAD+MID SHAPE (LOD-uniformity fix, user-directive 2026-05-30). The Proland
710
+ // ---- CONTINUOUS BROAD+MID SHAPE (LOD-uniformity fix). The
716
711
  // per-level g_noiseAmp cascade adds a DIFFERENT noise draw at each LOD, so consecutive levels
717
712
  // look different (1500km vs 1200km "way different"). The fix: source the broad+regional SHAPE
718
713
  // from ONE continuous fBm of WORLD DIRECTION, sampled the same at every LOD -> a finer level is
@@ -966,7 +961,8 @@ void main() {
966
961
  float dHdvN = float(hFD2 - h) / nStep;
967
962
  vNrm = normalize(uxN * (-dHduN) + uyN * (-dHdvN) + uzN); // n = normalize([-dz/du,-dz/dv,1]) in the (ux,uy,uz) frame
968
963
  }
969
- // DIRECT per-vertex sphere projection (replaces the Proland deformedCorners*alphaPrime blend,
964
+ // DIRECT per-vertex sphere projection (replaces the
965
+ deformedCorners*alphaPrime blend,
970
966
  // which bilinearly interpolated 4 deformed corners -> FLAT quad interior -> faceted at high GRID).
971
967
  // dir0 is THIS vertex's world direction (faceWarp'd, defLocalToWorld-mapped); place it on the
972
968
  // sphere at radius R+h and project. Every vertex curves -> round at any tessellation.
@@ -2076,7 +2072,7 @@ void main() {
2076
2072
  // ---- HEIGHT PROBE (collision): compute the EXACT rendered terrain height for one world dir,
2077
2073
  // reusing the same hpfSample + broadShapeM the mesh VS uses, so the free-fly collision floor
2078
2074
  // can never diverge from the rendered surface (user-chosen: read px from the GPU, not a CPU
2079
- // mirror). Paired with a 1-point VS in proland-gl-render.js; writes height (metres) to R32F.
2075
+ // mirror). Paired with a 1-point VS in gl-render.js; writes height (metres) to R32F.
2080
2076
  #ifdef _PROBE_
2081
2077
  uniform vec3 probeDir; // world direction under the camera (normalized)
2082
2078
  out vec4 probeOut;