leaflet-city-mapper 0.0.0-stage → 0.0.1

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.
Files changed (88) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +76 -2
  3. package/cucumber.mjs +5 -0
  4. package/dist/leaflet-city-mapper.js +2 -0
  5. package/dist/leaflet-city-mapper.js.map +7 -0
  6. package/dist/scene/gpu.js +3 -0
  7. package/dist/scene/gpu.js.map +7 -0
  8. package/dist/scene/lighting.js +9 -0
  9. package/dist/scene/lighting.js.map +7 -0
  10. package/dist/scene/model.js +2 -0
  11. package/dist/scene/model.js.map +7 -0
  12. package/dist/scene/picking.js +2 -0
  13. package/dist/scene/picking.js.map +7 -0
  14. package/dist/scene/pmtiles.js +2 -0
  15. package/dist/scene/pmtiles.js.map +7 -0
  16. package/dist/scene/render.js +15 -0
  17. package/dist/scene/render.js.map +7 -0
  18. package/dist/scene/sources.js +2 -0
  19. package/dist/scene/sources.js.map +7 -0
  20. package/dist/types/3d.js +2 -0
  21. package/dist/types/3d.js.map +7 -0
  22. package/dist/types/geojson.js +2 -0
  23. package/dist/types/geojson.js.map +7 -0
  24. package/dist/types/pmtiles.js +2 -0
  25. package/dist/types/pmtiles.js.map +7 -0
  26. package/dist/types/sunlight.js +2 -0
  27. package/dist/types/sunlight.js.map +7 -0
  28. package/dist/types/terrain.js +24 -0
  29. package/dist/types/terrain.js.map +7 -0
  30. package/dist/types/traffic.js +41 -0
  31. package/dist/types/traffic.js.map +7 -0
  32. package/dist/types/walk.js +2 -0
  33. package/dist/types/walk.js.map +7 -0
  34. package/dist/utils/control.js +2 -0
  35. package/dist/utils/control.js.map +7 -0
  36. package/dist/utils/geometry.js +2 -0
  37. package/dist/utils/geometry.js.map +7 -0
  38. package/dist/utils/lighting.js +2 -0
  39. package/dist/utils/lighting.js.map +7 -0
  40. package/dist/utils/model.js +2 -0
  41. package/dist/utils/model.js.map +7 -0
  42. package/dist/utils/picking.js +2 -0
  43. package/dist/utils/picking.js.map +7 -0
  44. package/dist/utils/pmtiles.js +2 -0
  45. package/dist/utils/pmtiles.js.map +7 -0
  46. package/dist/utils/scene.js +2 -0
  47. package/dist/utils/scene.js.map +7 -0
  48. package/dist/utils/shaders.js +203 -0
  49. package/dist/utils/shaders.js.map +7 -0
  50. package/dist/utils/sun.js +2 -0
  51. package/dist/utils/sun.js.map +7 -0
  52. package/dist/utils/terrain.js +2 -0
  53. package/dist/utils/terrain.js.map +7 -0
  54. package/dist/utils/traffic.js +2 -0
  55. package/dist/utils/traffic.js.map +7 -0
  56. package/dist/utils/vehicles.js +2 -0
  57. package/dist/utils/vehicles.js.map +7 -0
  58. package/dist/utils/walk.js +2 -0
  59. package/dist/utils/walk.js.map +7 -0
  60. package/package.json +45 -4
  61. package/src/index.js +82 -0
  62. package/src/scene/gpu.js +438 -0
  63. package/src/scene/lighting.js +193 -0
  64. package/src/scene/model.js +204 -0
  65. package/src/scene/picking.js +150 -0
  66. package/src/scene/pmtiles.js +94 -0
  67. package/src/scene/render.js +467 -0
  68. package/src/scene/sources.js +265 -0
  69. package/src/types/3d.js +337 -0
  70. package/src/types/geojson.js +42 -0
  71. package/src/types/pmtiles.js +143 -0
  72. package/src/types/sunlight.js +142 -0
  73. package/src/types/terrain.js +331 -0
  74. package/src/types/traffic.js +356 -0
  75. package/src/types/walk.js +219 -0
  76. package/src/utils/control.js +18 -0
  77. package/src/utils/geometry.js +256 -0
  78. package/src/utils/lighting.js +51 -0
  79. package/src/utils/model.js +178 -0
  80. package/src/utils/picking.js +119 -0
  81. package/src/utils/pmtiles.js +79 -0
  82. package/src/utils/scene.js +102 -0
  83. package/src/utils/shaders.js +237 -0
  84. package/src/utils/sun.js +71 -0
  85. package/src/utils/terrain.js +78 -0
  86. package/src/utils/traffic.js +90 -0
  87. package/src/utils/vehicles.js +69 -0
  88. package/src/utils/walk.js +72 -0
package/README.md CHANGED
@@ -1,3 +1,77 @@
1
- # Temporary Holding Version
1
+ # leaflet-city-mapper
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![NPM version](https://img.shields.io/npm/v/leaflet-city-builder.svg?color=red)](https://www.npmjs.com/package/leaflet-city-builder)
4
+ [![License](https://img.shields.io/badge/license-GPL%203-blue.svg?style=flat)](LICENSE)
5
+
6
+ **Your data, on a rotated and pitched Leaflet map:** a small 3D rendering engine (WebGPU) packaged onto standard `L.Layer`. Available sources: extruded GeoJSON, glTF models, PMTiles and terrain layers.
7
+
8
+ <p align="center">
9
+ <a href="https://raruto.github.io/leaflet-city-mapper/examples/leaflet-city-mapper.html"><img src="https://raruto.github.io/img/leaflet-city-mapper.png" alt="Leaflet 3D City Mapper" /></a>
10
+ </p>
11
+
12
+ _For a working example see one of the following demos:_
13
+
14
+ - [City of Paris in 3D](https://raruto.github.io/leaflet-city-mapper/examples/leaflet-city-mapper.html)
15
+ - [Ancient Rome in 3D](https://raruto.github.io/leaflet-city-mapper/examples/leaflet-city-mapper-geojson.html)
16
+
17
+ ## How to use
18
+
19
+ Load leaflet and leaflet-rotate first, then this plugin:
20
+
21
+
22
+ ```html
23
+ <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9/dist/leaflet.css">
24
+ <script src="https://unpkg.com/leaflet@1.9/dist/leaflet.js"></script>
25
+ <script src="https://unpkg.com/leaflet-rotate@0.5.3/dist/leaflet-rotate.js"></script>
26
+ <script src="https://unpkg.com/leaflet-city-mapper@0.0.1/dist/leaflet-city-mapper.js"></script>
27
+ ```
28
+ And then set an approriate `source` param to layer constructor:
29
+
30
+ ```js
31
+ new L.Layer({ source: .. })
32
+ ```
33
+ For example, here you can use a `geojson` object, like this:
34
+
35
+ ```js
36
+ const buildings = new L.Layer({
37
+ source: {
38
+ type: 'geojson',
39
+ url: GeoJSON,
40
+ featureStyle: ({ properties }) => ({ top: properties.height, color: properties.color }),
41
+ tooltipContent: ({ properties }) => `${properties.name}: ${properties.height} m`,
42
+ },
43
+ }).addTo(map);
44
+ ```
45
+
46
+ After that, the [`dist/types/geojson.js`](./src/types/geojson.js) will be dynamically imported into the page and a 3D layer should appear onto the map.
47
+
48
+ For further customization, you can define custom [`types`](./src/types/geojson.js): just upload them to the same folder and they will be loaded automatically.
49
+
50
+ ### Update demo data
51
+
52
+ The Paris example makes extensive use of [PMTiles](./build/pmtiles.js) to ensure offline access / long-term availability of the map. To update them:
53
+
54
+ ```shell
55
+ npm run pmtiles
56
+ node build/pmtiles.js --only=trees
57
+ ```
58
+
59
+ ### Build Guide
60
+
61
+ Within your local development environment:
62
+
63
+ ```shell
64
+ git clone git@github.com:Raruto/leaflet-city-builder.git
65
+ cd ./leaflet-city-builder
66
+
67
+ npm i # install dependencies
68
+ npm run dev # start dev server at: http://localhost:8080
69
+ npm run build # generate "dist" files (once)
70
+ npm run test # build and run browser tests
71
+ ```
72
+
73
+ After that you can start developing inside the `src` and `test` folders (eg. open "http://localhost:8080" in your browser to preview changes).
74
+
75
+
76
+ **Compatibile with:**
77
+ [![Leaflet 1.9.4 compatible!](https://img.shields.io/badge/Leaflet-1.9.4-1EB300.svg?style=flat)](http://leafletjs.com/reference.html) [![Leaflet 2.x compatible!](https://img.shields.io/badge/Leaflet-2.0.0--alpha.1-1EB300.svg?style=flat)](http://leafletjs.com/reference.html)
package/cucumber.mjs ADDED
@@ -0,0 +1,5 @@
1
+ export default {
2
+ paths: ['test/features/**/*.feature'],
3
+ import: ['test/support/**/*.js', 'test/steps/**/*.js'],
4
+ format: ['progress'],
5
+ };
@@ -0,0 +1,2 @@
1
+ const a=Object.defineProperties({},Object.getOwnPropertyDescriptors(L.Layer.prototype)),l=L.Layer.extend({onAdd(){},onRemove(){}});let p=Promise.resolve();L.Layer.types={},L.Layer.include({initialize(r={}){if(Object.getPrototypeOf(this)!==L.Layer.prototype||!r.source){if(a.initialize)return a.initialize.apply(this,arguments);L.Util.setOptions(this,r);return}const e=this,{source:{type:t=/\.pmtiles(?:[?#]|$)/i.test(r.source.url)?"pmtiles":"3d",...n},...y}=r,i={...n,...y},o=e._map;if(o?.removeLayer(e),delete e.onAdd,delete e.onRemove,L.Layer.types[t]){Object.setPrototypeOf(e,L.Layer.types[t].prototype),e.options=Object.create(L.Layer.types[t].prototype.options),L.Layer.types[t].prototype.initialize.call(e,i),o?.addLayer(e);return}Object.setPrototypeOf(e,l.prototype),L.Util.setOptions(e,y),o?.addLayer(e),p=p.catch(()=>{}).then(()=>import(`./types/${t}.js`));const c=e.ready=p.then(()=>{const s=e._map;return s?.removeLayer(e),Object.setPrototypeOf(e,L.Layer.types[t].prototype),e.options=Object.create(L.Layer.types[t].prototype.options),L.Layer.types[t].prototype.initialize.call(e,i),s?.addLayer(e),e.ready===c?e:e.ready})}});
2
+ //# sourceMappingURL=leaflet-city-mapper.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.js"],
4
+ "sourcesContent": ["/**\n * `new L.Layer({ source: { type, url, ...settings }, pane, attribution })`: layers with a `source`\n * load their type module on first use. Subclasses are unchanged.\n */\n\n/**\n * Router for `new L.Layer({ source: { type, url, ...settings }, pane, attribution })`.\n * It imports the type module on demand and turns the\n * layer into it; types receive flat options (`{ ...source, pane, attribution }`), so `layer.options.url` etc.\n *\n * Folder layout:\n * - types/: one module per `source.type`, the only files imported by this router:\n * - 'pmtiles': raster PMTiles basemap (an L.TileLayer), draped on the terrain when present;\n * - '3d': shared WebGPU scene with a GLB/glTF model, PMTiles GeoJSON features, or empty;\n * - 'geojson': inline or URL-backed GeoJSON features rendered by the shared 3D scene;\n * - 'terrain': Float32 PMTiles DEM that deforms raster tiles, buildings and the camera, plus optional bridge decks;\n * - 'sunlight': simulation clock control, sun/moon lighting and shadows;\n * - 'traffic': vehicles moving along GeoJSON lanes (measured flow or timetable);\n * - 'walk': third-person pedestrian moved with the keyboard, the camera following behind.\n * - scene/: named operations for GPU resources, model/archive loading, lighting, sources, picking\n * and rendering. They receive the layer explicitly; types/3d.js declares its delegates, without mixins.\n * - utils/: computations and scene-data helpers, independent of Leaflet layers and GPU devices.\n * Geometry, decoders, lighting grids, terrain sampling, traffic and collisions can be tested in Node;\n * scene transforms/picking use browser DOMMatrix/DOMPoint, and CSS colors use a canvas.\n * Dataset-specific code and data (routes, timetables, API loaders) belong to the application, e.g. misc/.\n *\n * When `type` is omitted it is inferred from `source.url`: *.pmtiles -> 'pmtiles', anything else -> '3d'.\n */\n\nconst layerProto = Object.defineProperties({}, Object.getOwnPropertyDescriptors(L.Layer.prototype));\n\n// Stand-in while the type module loads: remembers the map, renders nothing.\nconst Pending = L.Layer.extend({\n onAdd() {},\n onRemove() {},\n});\n\n// One chain for every pending layer: they become real layers in creation order, whatever the import timing.\nlet loading = Promise.resolve();\n\n// Shared registry: type modules (src/types) register here.\nL.Layer.types = {};\n\n// Core L.Layer has no initialize and init hooks get no arguments: keep the options for the hook.\nL.Layer.include({\n initialize(options = {}) {\n if (Object.getPrototypeOf(this) !== L.Layer.prototype || !options.source) {\n if (layerProto.initialize) return layerProto.initialize.apply(this, arguments);\n L.Util.setOptions(this, options);\n return;\n }\n const layer = this;\n const { source: { type = /\\.pmtiles(?:[?#]|$)/i.test(options.source.url) ? 'pmtiles' : '3d', ...source }, ...core } = options;\n const settings = { ...source, ...core };\n // A stand-in created by the entry point before this module loaded may already be on a map.\n const map = layer._map;\n map?.removeLayer(layer);\n delete layer.onAdd;\n delete layer.onRemove;\n if (L.Layer.types[type]) {\n Object.setPrototypeOf(layer, L.Layer.types[type].prototype);\n layer.options = Object.create(L.Layer.types[type].prototype.options);\n L.Layer.types[type].prototype.initialize.call(layer, settings);\n map?.addLayer(layer);\n return;\n }\n Object.setPrototypeOf(layer, Pending.prototype);\n L.Util.setOptions(layer, core);\n map?.addLayer(layer);\n // ponytail: type methods (setOpacity, add, ...) exist only once `ready` resolves.\n loading = loading.catch(() => {}).then(() => import(`./types/${type}.js`));\n const pending = layer.ready = loading.then(() => {\n const map = layer._map;\n map?.removeLayer(layer);\n Object.setPrototypeOf(layer, L.Layer.types[type].prototype);\n layer.options = Object.create(L.Layer.types[type].prototype.options);\n L.Layer.types[type].prototype.initialize.call(layer, settings);\n map?.addLayer(layer);\n return layer.ready === pending ? layer : layer.ready;\n });\n },\n});"],
5
+ "mappings": "AA6BA,MAAMA,EAAa,OAAO,iBAAiB,CAAC,EAAG,OAAO,0BAA0B,EAAE,MAAM,SAAS,CAAC,EAG5FC,EAAU,EAAE,MAAM,OAAO,CAC7B,OAAQ,CAAC,EACT,UAAW,CAAC,CACd,CAAC,EAGD,IAAIC,EAAU,QAAQ,QAAQ,EAG9B,EAAE,MAAM,MAAQ,CAAC,EAGjB,EAAE,MAAM,QAAQ,CACd,WAAWC,EAAU,CAAC,EAAG,CACvB,GAAI,OAAO,eAAe,IAAI,IAAM,EAAE,MAAM,WAAa,CAACA,EAAQ,OAAQ,CACxE,GAAIH,EAAW,WAAY,OAAOA,EAAW,WAAW,MAAM,KAAM,SAAS,EAC7E,EAAE,KAAK,WAAW,KAAMG,CAAO,EAC/B,MACF,CACA,MAAMC,EAAQ,KACR,CAAE,OAAQ,CAAE,KAAAC,EAAO,uBAAuB,KAAKF,EAAQ,OAAO,GAAG,EAAI,UAAY,KAAM,GAAGG,CAAO,EAAG,GAAGC,CAAK,EAAIJ,EAChHK,EAAW,CAAE,GAAGF,EAAQ,GAAGC,CAAK,EAEhCE,EAAML,EAAM,KAIlB,GAHAK,GAAK,YAAYL,CAAK,EACtB,OAAOA,EAAM,MACb,OAAOA,EAAM,SACT,EAAE,MAAM,MAAMC,CAAI,EAAG,CACvB,OAAO,eAAeD,EAAO,EAAE,MAAM,MAAMC,CAAI,EAAE,SAAS,EAC1DD,EAAM,QAAU,OAAO,OAAO,EAAE,MAAM,MAAMC,CAAI,EAAE,UAAU,OAAO,EACnE,EAAE,MAAM,MAAMA,CAAI,EAAE,UAAU,WAAW,KAAKD,EAAOI,CAAQ,EAC7DC,GAAK,SAASL,CAAK,EACnB,MACF,CACA,OAAO,eAAeA,EAAOH,EAAQ,SAAS,EAC9C,EAAE,KAAK,WAAWG,EAAOG,CAAI,EAC7BE,GAAK,SAASL,CAAK,EAEnBF,EAAUA,EAAQ,MAAM,IAAM,CAAC,CAAC,EAAE,KAAK,IAAM,OAAO,WAAWG,CAAI,MAAM,EACzE,MAAMK,EAAUN,EAAM,MAAQF,EAAQ,KAAK,IAAM,CAC/C,MAAMO,EAAML,EAAM,KAClB,OAAAK,GAAK,YAAYL,CAAK,EACtB,OAAO,eAAeA,EAAO,EAAE,MAAM,MAAMC,CAAI,EAAE,SAAS,EAC1DD,EAAM,QAAU,OAAO,OAAO,EAAE,MAAM,MAAMC,CAAI,EAAE,UAAU,OAAO,EACnE,EAAE,MAAM,MAAMA,CAAI,EAAE,UAAU,WAAW,KAAKD,EAAOI,CAAQ,EAC7DC,GAAK,SAASL,CAAK,EACZA,EAAM,QAAUM,EAAUN,EAAQA,EAAM,KACjD,CAAC,CACH,CACF,CAAC",
6
+ "names": ["layerProto", "Pending", "loading", "options", "layer", "type", "source", "core", "settings", "map", "pending"]
7
+ }
@@ -0,0 +1,3 @@
1
+ import{materialShaders as _}from"../utils/shaders.js";import{emptyNode as U,setParent as w,traverse as G}from"../utils/scene.js";async function B(r,e){let t=L.Layer.types["3d"]._renderers.get(e);if(t?._rendererReady&&await t._rendererReady.promise,!t){const o=Promise.withResolvers();t=r,r._rendererReady=o,o.promise.catch(()=>{}),L.Layer.types["3d"]._renderers.set(e,t);const x=l=>{throw o.reject(l),L.Layer.types["3d"]._renderers.delete(e),l},g=document.createElement("canvas");navigator.gpu||x(new Error("Layer3D requires WebGPU"));const f=await navigator.gpu.requestAdapter().catch(x);f||x(new Error("Unable to acquire a WebGPU adapter"));const i=await f.requestDevice().catch(x);i.addEventListener("uncapturederror",l=>{const p=L.Layer.types["3d"]._renderers.get(e);if(p?.renderer?.device===i)for(const v of p?._layers||[])v.fire("error",{error:l.error})}),i.lost.then(l=>{const p=L.Layer.types["3d"]._renderers.get(e);if(!p||p.renderer?.device!==i)return;const v=new Error(`WebGPU device lost: ${l.message||l.reason}`);for(const c of p._layers)c.fire("error",{error:v})});const u=g.getContext("webgpu");u||(i.destroy(),x(new Error("Unable to create a WebGPU canvas context")));const n=navigator.gpu.getPreferredCanvasFormat();u.configure({device:i,format:n,alphaMode:"premultiplied"}),r._shared=r,r._layers=new Set,r._sceneOrigin=r.location,r.scene=U(),r.camera={position:[0,0,0],up:[0,1,0],target:[0,0,0],viewMatrix:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],projectionMatrix:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],projectionViewMatrix:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],fov:45,aspect:1,near:.1,far:1e4},r.renderer={device:i,context:u,format:n,canvas:g,dpr:Math.min(window.devicePixelRatio,2)},r._initSunlight();const s=document.createElement("canvas");s.width=1,s.height=1,r.renderer.roofFallbackTexture={device:i,texture:i.createTexture({size:[1,1],format:"rgba8unorm",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT}),sampler:i.createSampler({addressModeU:"clamp-to-edge",addressModeV:"clamp-to-edge"}),image:s,format:"rgba8unorm",srgb:!1,uploaded:null},t._size=null,g.className="leaflet-3d",g.style.cssText="position:absolute;top:0;left:0;pointer-events:none",g.setAttribute("role","img"),g.setAttribute("aria-label",r.options.ariaLabel);let a=e.getPane("webgpuPane");a||(a=e.createPane("webgpuPane",e.getContainer()),a.style.zIndex=450),a.append(g),e.on("move zoom rotate pitch resize viewreset layeradd layerremove",t._onMapChange,t),e.on("zoomstart",t._startZoom,t),e.on("zoomanim",t.update,t),e.on("zoomend",t._endZoom,t),o.resolve(),delete t._rendererReady,t._render(),L.Layer.types["3d"]._renderers.set(e,t)}const y=e.options.crs.project(t._sceneOrigin),h=e.options.crs.project(r.location),d=Math.cos(t._sceneOrigin.lat*Math.PI/180);r._terrainBaseY??=r.root.position[1],r.root.position[0]=(h.x-y.x)*d,r.root.position[1]=r._terrainBaseY,r.root.position[2]=-(h.y-y.y)*d,r._setTerrainHeight(),r.root.scale.fill(d/Math.cos(r.location.lat*Math.PI/180)),r._shared=t,r.renderer=t.renderer,r.scene=t.scene,r.camera=t.camera,t._layers.add(r),w(r.root,t.scene),G(r.root,o=>{if(o.geometry&&o.geometry.device!==t.renderer.device){const x=o.geometry,g=o.program._layerMaterial,f={};for(const u of["position","normal","color","uv","index"]){const n=x.attributes[u];if(!n)continue;const{data:s,size:a,count:l,normalized:p}=n;f[u]={data:s.slice(0,l*a),size:a,normalized:p}}const i=t.renderer.device;f.index?.data instanceof Uint8Array&&(f.index.data=Uint16Array.from(f.index.data));for(const[u,n]of Object.entries(f))n.size||=1,n.count=n.data.length/n.size,n.indexFormat=n.data instanceof Uint32Array?"uint32":"uint16",n.buffer=i.createBuffer({size:Math.max(4,n.data.byteLength),usage:u==="index"?GPUBufferUsage.INDEX|GPUBufferUsage.COPY_DST:GPUBufferUsage.VERTEX|GPUBufferUsage.COPY_DST}),i.queue.writeBuffer(n.buffer,0,n.data);o.geometry={device:i,attributes:f,drawRange:{start:0,count:f.index?.count||f.position.count}};for(const u of Object.values(x.attributes))u.buffer.destroy();o.bindGroup=null,o.boundTextures=[];for(const u of["baseTexture","metalTexture","emissiveTexture","occlusionTexture"]){const n=g[u];!n||n.device===i||(g[u]={...n,device:i,texture:i.createTexture({size:[n.image.width,n.image.height],format:n.format,usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT}),sampler:i.createSampler(n.samplerOptions),uploaded:null})}r._removeProgram(o.program),o.program=r._createProgram(g),o.device=i}o.texture&&o.texture.device!==t.renderer.device&&(o.texture={...o.texture,device:t.renderer.device,texture:t.renderer.device.createTexture({size:[o.texture.image.width,o.texture.image.height],format:o.texture.format,usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT}),sampler:t.renderer.device.createSampler(o.texture.samplerOptions),uploaded:null})}),t.update(),(r.header||r._geojson)&&r._updateArea()}function M(r,e){r.renderer.device.queue.onSubmittedWorkDone().then(()=>e.destroy(),t=>{r.fire("error",{error:t}),e.destroy()})}function R(r,e={}){const t=r.renderer.device,y=!!r.options.nightSparkle,h=_(e,y,e.terrain?L.Layer.types.terrain.elevationWGSL:""),{bindings:d,textured:o,hasTerrain:x}=h,g=t.createShaderModule({code:h.vertex}),f=t.createShaderModule({code:h.fragment});Promise.all([g.getCompilationInfo(),f.getCompilationInfo()]).then(a=>{const l=a.flatMap(p=>p.messages).filter(p=>p.type==="error");l.length&&r.fire("error",{error:new Error(`Unable to compile Layer3D WGSL: ${l.map(p=>p.message).join(`
2
+ `)}`)})}).catch(a=>r.fire("error",{error:a}));const i=[{arrayStride:12,attributes:[{shaderLocation:0,offset:0,format:"float32x3"}]},{arrayStride:12,attributes:[{shaderLocation:1,offset:0,format:"float32x3"}]},e.vertexColors?{arrayStride:12,attributes:[{shaderLocation:2,offset:0,format:"float32x3"}]}:null,o?{arrayStride:8,attributes:[{shaderLocation:3,offset:0,format:"float32x2"}]}:null],u=[{binding:0,visibility:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT,buffer:{type:"uniform",hasDynamicOffset:!0}},{binding:1,visibility:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT,buffer:{type:"uniform"}}];x&&u.push({binding:2,visibility:GPUShaderStage.VERTEX,texture:{sampleType:"unfilterable-float",viewDimension:"2d"}});for(const a of d)u.push({binding:a.texture,visibility:GPUShaderStage.FRAGMENT,texture:{sampleType:"float",viewDimension:"2d"}}),u.push({binding:a.sampler,visibility:GPUShaderStage.FRAGMENT,sampler:{type:"filtering"}});const n=t.createBindGroupLayout({entries:u}),s=t.createPipelineLayout({bindGroupLayouts:[n,r.renderer.sunlight.layout]});return{device:t,vertex:g,fragment:f,bindings:d,vertexBuffers:i,bindGroupLayout:n,pipelineLayout:s,pipelineCache:new Map,cullMode:e.doubleSided?"none":"back",transparent:e.alphaMode==="BLEND",depthWrite:e.alphaMode!=="BLEND",depthTest:e.depthTest!==!1,terrain:e.terrain,nightLight:!!e.nightLight,nightSparkle:y,uniforms:{baseColor:{value:e.color||[1,1,1]},metallic:{value:e.metallic??0},roughness:{value:e.roughness??.85},opacity:{value:e.opacity??1},alphaCutoff:{value:e.alphaCutoff??.5},occlusionStrength:{value:e.occlusionStrength??1},emissive:{value:e.emissive||[0,0,0]},...e.baseTexture?{baseTexture:{value:e.baseTexture}}:{},...e.metalTexture?{metalTexture:{value:e.metalTexture}}:{},...e.emissiveTexture?{emissiveTexture:{value:e.emissiveTexture}}:{},...e.occlusionTexture?{occlusionTexture:{value:e.occlusionTexture}}:{},...e.roofTexture?{roofTexture:{value:null}}:{}},_layerMaterial:e}}function C(r){r.pipelineCache.clear()}function D(r,e,t={},y=0){const{device:h}=r.renderer,d={};for(const[x,g]of Object.entries(e)){const f=new Float32Array(g),i=h.createBuffer({size:Math.max(4,f.byteLength),usage:GPUBufferUsage.VERTEX|GPUBufferUsage.COPY_DST|y});h.queue.writeBuffer(i,0,f),d[x]={size:3,data:f,count:f.length/3,buffer:i}}const o={...U(),device:h,geometry:{device:h,attributes:d,drawRange:{start:0,count:d.position.count}},program:r._createProgram({vertexColors:!0,doubleSided:!0,...t}),id:L.Layer.types["3d"]._nextId++,renderOrder:0,extras:{}};return r.add(o),o}function N(r,e){if(e){r.remove(e);for(const t of Object.values(e.geometry.attributes))t.buffer.destroy();r._removeProgram(e.program)}}function z(r,e,t,y,h){const{geometry:d,program:o}=e,x=r.renderer.device,g=`${y}:${h}`;let f=o.pipelineCache.get(g);f||(f=x.createRenderPipeline({layout:o.pipelineLayout,vertex:{module:o.vertex,entryPoint:"vertexMain",buffers:o.vertexBuffers},fragment:{module:o.fragment,entryPoint:"fragmentMain",targets:[{format:r.renderer.format,blend:o.transparent?{color:{srcFactor:"src-alpha",dstFactor:"one-minus-src-alpha",operation:"add"},alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha",operation:"add"}}:void 0,writeMask:GPUColorWrite.ALL}]},primitive:{topology:"triangle-list",cullMode:y,frontFace:h},depthStencil:{format:"depth24plus",depthWriteEnabled:o.depthWrite,depthCompare:o.depthTest===!1?"always":"less-equal"}}),o.pipelineCache.set(g,f));const i=o.uniforms,u=r.renderer,n=u.uniformSlot++*256,s=u.uniformData,a=n/4;s.set(e.worldMatrix,a),s.set(i.baseColor.value,a+16),s[a+19]=1,s[a+20]=i.metallic.value,s[a+21]=i.roughness.value,s[a+22]=i.opacity.value,s[a+23]=i.alphaCutoff.value,s.set(i.emissive.value,a+24),s[a+27]=i.occlusionStrength.value,s[a+28]=e.terrain?.[0]??0,s[a+29]=e.terrain?.[1]??0,s[a+30]=e.terrain?.[2]??0,s[a+31]=e.terrain?.[3]??0,s[a+32]=e.highlight?.[0]??0,s[a+33]=e.highlight?.[1]??0,s[a+34]=o.nightSparkle?s[55]:0,s[a+35]=0;const l=e._textures||=[];l.length=0;for(const c of o.bindings){const m=i[c.name]?.value;if(!m)throw new Error(`Missing Layer3D texture: ${c.name}`);m.uploaded!==m.image&&(x.queue.copyExternalImageToTexture({source:m.image,flipY:!!m.flipY},{texture:m.texture},[m.image.width,m.image.height]),m.uploaded=m.image),l.push(m)}let p=!e.bindGroup||e.boundBuffer!==u.uniformBuffer||l.length!==e.boundTextures.length;for(let c=0;!p&&c<l.length;c++)(l[c].texture!==e.boundTextures[c][0]||l[c].sampler!==e.boundTextures[c][1])&&(p=!0);if(p){const c=[{binding:0,resource:{buffer:u.uniformBuffer,size:144}},{binding:1,resource:{buffer:u.uniformBuffer,size:240}}];o.terrain&&c.push({binding:2,resource:o.terrain.texture.createView()}),e.boundBuffer=u.uniformBuffer;const m=e.boundTextures||=[];m.length=l.length;for(let T=0;T<l.length;T++){const P=o.bindings[T],b=l[T];c.push({binding:P.texture,resource:b.texture.createView()}),c.push({binding:P.sampler,resource:b.sampler}),m[T]||=[],m[T][0]=b.texture,m[T][1]=b.sampler}e.bindGroup=x.createBindGroup({layout:o.bindGroupLayout,entries:c})}t.setPipeline(f),t.setBindGroup(0,e.bindGroup,[n]),t.setVertexBuffer(0,d.attributes.position.buffer),t.setVertexBuffer(1,d.attributes.normal.buffer),d.attributes.color&&t.setVertexBuffer(2,d.attributes.color.buffer),d.attributes.uv&&t.setVertexBuffer(3,d.attributes.uv.buffer);for(const c of Object.values(d.attributes))c.needsUpdate&&(x.queue.writeBuffer(c.buffer,0,c.data),c.needsUpdate=!1,d.bounds=null);const v=d.attributes.index;v?(t.setIndexBuffer(v.buffer,v.indexFormat),t.drawIndexed(d.drawRange.count,1,d.drawRange.start,0,0)):t.draw(d.drawRange.count,1,d.drawRange.start,0)}export{B as attach,D as createMesh,R as createProgram,M as destroyTexture,N as disposeMesh,z as drawGeometry,C as removeProgram};
3
+ //# sourceMappingURL=gpu.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/scene/gpu.js"],
4
+ "sourcesContent": ["import { materialShaders } from '../utils/shaders.js';\nimport { emptyNode, setParent, traverse } from '../utils/scene.js';\n\n/** WebGPU device attachment, materials/pipelines and draw calls. */\n/**\n * Join/create this map's shared scene and rebuild GPU objects if its context changed.\n * Buffer/program handles belong to one context; typed arrays and decoded images do not.\n * @private\n */\nexport async function attach(backend, map) {\n let shared = L.Layer.types['3d']._renderers.get(map);\n if (shared?._rendererReady) {\n await shared._rendererReady.promise;\n }\n if (!shared) {\n const rendererReady = Promise.withResolvers();\n shared = backend;\n backend._rendererReady = rendererReady;\n rendererReady.promise.catch(() => {});\n L.Layer.types['3d']._renderers.set(map, shared);\n const fail = error => {\n rendererReady.reject(error);\n L.Layer.types['3d']._renderers.delete(map);\n throw error;\n };\n const canvas = document.createElement('canvas');\n if (!navigator.gpu) fail(new Error('Layer3D requires WebGPU'));\n const adapter = await navigator.gpu.requestAdapter().catch(fail);\n if (!adapter) fail(new Error('Unable to acquire a WebGPU adapter'));\n const device = await adapter.requestDevice().catch(fail);\n device.addEventListener('uncapturederror', event => {\n const owner = L.Layer.types['3d']._renderers.get(map);\n if (owner?.renderer?.device !== device) return;\n for (const layer of owner?._layers || []) layer.fire('error', { error: event.error });\n });\n device.lost.then(info => {\n const owner = L.Layer.types['3d']._renderers.get(map);\n if (!owner || owner.renderer?.device !== device) return;\n const error = new Error(`WebGPU device lost: ${info.message || info.reason}`);\n for (const layer of owner._layers) layer.fire('error', { error });\n });\n const context = canvas.getContext('webgpu');\n if (!context) {\n device.destroy();\n fail(new Error('Unable to create a WebGPU canvas context'));\n }\n const format = navigator.gpu.getPreferredCanvasFormat();\n context.configure({ device, format, alphaMode: 'premultiplied' });\n backend._shared = backend;\n backend._layers = new Set();\n backend._sceneOrigin = backend.location;\n backend.scene = emptyNode();\n // Perspective camera state: fov in degrees, near/far in scene metres.\n backend.camera = {\n position: [0, 0, 0],\n up: [0, 1, 0],\n target: [0, 0, 0],\n viewMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],\n projectionMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],\n projectionViewMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],\n fov: 45,\n aspect: 1,\n near: 0.1,\n far: 10000,\n };\n backend.renderer = { device, context, format, canvas, dpr: Math.min(window.devicePixelRatio, 2) };\n backend._initSunlight();\n const emptyImage = document.createElement('canvas');\n emptyImage.width = 1;\n emptyImage.height = 1;\n backend.renderer.roofFallbackTexture = {\n device,\n texture: device.createTexture({\n size: [1, 1],\n format: 'rgba8unorm',\n usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,\n }),\n sampler: device.createSampler({\n addressModeU: 'clamp-to-edge', addressModeV: 'clamp-to-edge',\n }),\n image: emptyImage, format: 'rgba8unorm', srgb: false, uploaded: null,\n };\n shared._size = null;\n canvas.className = 'leaflet-3d';\n canvas.style.cssText = 'position:absolute;top:0;left:0;pointer-events:none';\n canvas.setAttribute('role', 'img');\n canvas.setAttribute('aria-label', backend.options.ariaLabel);\n let pane = map.getPane('webgpuPane');\n if (!pane) {\n pane = map.createPane('webgpuPane', map.getContainer());\n pane.style.zIndex = 450;\n }\n pane.append(canvas);\n map.on('move zoom rotate pitch resize viewreset layeradd layerremove', shared._onMapChange, shared);\n map.on('zoomstart', shared._startZoom, shared);\n map.on('zoomanim', shared.update, shared);\n map.on('zoomend', shared._endZoom, shared);\n rendererReady.resolve();\n delete shared._rendererReady;\n shared._render();\n L.Layer.types['3d']._renderers.set(map, shared);\n }\n const origin = map.options.crs.project(shared._sceneOrigin);\n const point = map.options.crs.project(backend.location);\n const metres = Math.cos(shared._sceneOrigin.lat * Math.PI / 180);\n backend._terrainBaseY ??= backend.root.position[1];\n backend.root.position[0] = (point.x - origin.x) * metres;\n backend.root.position[1] = backend._terrainBaseY;\n backend.root.position[2] = -(point.y - origin.y) * metres;\n backend._setTerrainHeight();\n // Each group's metres are measured at its own latitude, not the shared camera origin.\n backend.root.scale.fill(metres / Math.cos(backend.location.lat * Math.PI / 180));\n backend._shared = shared;\n backend.renderer = shared.renderer;\n backend.scene = shared.scene;\n backend.camera = shared.camera;\n shared._layers.add(backend);\n setParent(backend.root, shared.scene);\n traverse(backend.root, object => {\n if (object.geometry && object.geometry.device !== shared.renderer.device) {\n const geometry = object.geometry;\n const material = object.program._layerMaterial;\n // Retain packed CPU attributes, never old-context GPU buffer handles.\n const attributes = {};\n for (const name of ['position', 'normal', 'color', 'uv', 'index']) {\n const attribute = geometry.attributes[name];\n if (!attribute) continue;\n const { data, size, count, normalized } = attribute;\n attributes[name] = { data: data.slice(0, count * size), size, normalized };\n }\n const device = shared.renderer.device;\n if (attributes.index?.data instanceof Uint8Array) {\n attributes.index.data = Uint16Array.from(attributes.index.data);\n }\n for (const [name, attribute] of Object.entries(attributes)) {\n attribute.size ||= 1;\n attribute.count = attribute.data.length / attribute.size;\n attribute.indexFormat = attribute.data instanceof Uint32Array ? 'uint32' : 'uint16';\n attribute.buffer = device.createBuffer({\n size: Math.max(4, attribute.data.byteLength),\n usage: name === 'index'\n ? GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST\n : GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\n });\n device.queue.writeBuffer(attribute.buffer, 0, attribute.data);\n }\n object.geometry = {\n device,\n attributes,\n drawRange: { start: 0, count: attributes.index?.count || attributes.position.count },\n };\n for (const attribute of Object.values(geometry.attributes)) attribute.buffer.destroy();\n object.bindGroup = null;\n object.boundTextures = [];\n for (const key of ['baseTexture', 'metalTexture', 'emissiveTexture', 'occlusionTexture']) {\n const texture = material[key];\n if (!texture || texture.device === device) continue;\n material[key] = {\n ...texture,\n device,\n texture: device.createTexture({\n size: [texture.image.width, texture.image.height],\n format: texture.format,\n usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,\n }),\n sampler: device.createSampler(texture.samplerOptions),\n uploaded: null,\n };\n }\n backend._removeProgram(object.program);\n object.program = backend._createProgram(material);\n object.device = device;\n }\n if (object.texture && object.texture.device !== shared.renderer.device) {\n object.texture = {\n ...object.texture,\n device: shared.renderer.device,\n texture: shared.renderer.device.createTexture({\n size: [object.texture.image.width, object.texture.image.height],\n format: object.texture.format,\n usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,\n }),\n sampler: shared.renderer.device.createSampler(object.texture.samplerOptions),\n uploaded: null,\n };\n }\n });\n shared.update();\n if (backend.header || backend._geojson) backend._updateArea();\n}\n\nexport function destroyTexture(backend, texture) {\n backend.renderer.device.queue.onSubmittedWorkDone().then(() => texture.destroy(), error => {\n backend.fire('error', { error });\n texture.destroy();\n });\n}\n\nexport function createProgram(backend, material = {}) {\n const device = backend.renderer.device;\n const sparkle = Boolean(backend.options.nightSparkle);\n const shaders = materialShaders(material, sparkle, material.terrain ? L.Layer.types.terrain.elevationWGSL : '');\n const { bindings, textured, hasTerrain } = shaders;\n const vertex = device.createShaderModule({ code: shaders.vertex });\n const fragment = device.createShaderModule({ code: shaders.fragment });\n Promise.all([vertex.getCompilationInfo(), fragment.getCompilationInfo()]).then(infos => {\n const errors = infos.flatMap(info => info.messages).filter(message => message.type === 'error');\n if (errors.length) {\n backend.fire('error', {\n error: new Error(`Unable to compile Layer3D WGSL: ${errors.map(error => error.message).join('\\n')}`),\n });\n }\n }).catch(error => backend.fire('error', { error }));\n const vertexBuffers = [\n { arrayStride: 12, attributes: [{ shaderLocation: 0, offset: 0, format: 'float32x3' }] },\n { arrayStride: 12, attributes: [{ shaderLocation: 1, offset: 0, format: 'float32x3' }] },\n material.vertexColors ? { arrayStride: 12, attributes: [{ shaderLocation: 2, offset: 0, format: 'float32x3' }] } : null,\n textured ? { arrayStride: 8, attributes: [{ shaderLocation: 3, offset: 0, format: 'float32x2' }] } : null,\n ];\n const bindGroupEntries = [{\n binding: 0,\n visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,\n buffer: { type: 'uniform', hasDynamicOffset: true },\n }, {\n binding: 1,\n visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,\n buffer: { type: 'uniform' },\n }];\n if (hasTerrain) {\n bindGroupEntries.push({\n binding: 2,\n visibility: GPUShaderStage.VERTEX,\n texture: { sampleType: 'unfilterable-float', viewDimension: '2d' },\n });\n }\n for (const texture of bindings) {\n bindGroupEntries.push({\n binding: texture.texture,\n visibility: GPUShaderStage.FRAGMENT,\n texture: { sampleType: 'float', viewDimension: '2d' },\n });\n bindGroupEntries.push({\n binding: texture.sampler,\n visibility: GPUShaderStage.FRAGMENT,\n sampler: { type: 'filtering' },\n });\n }\n const bindGroupLayout = device.createBindGroupLayout({ entries: bindGroupEntries });\n const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout, backend.renderer.sunlight.layout] });\n return {\n device,\n vertex,\n fragment,\n bindings,\n vertexBuffers,\n bindGroupLayout,\n pipelineLayout,\n pipelineCache: new Map(),\n cullMode: material.doubleSided ? 'none' : 'back',\n transparent: material.alphaMode === 'BLEND',\n depthWrite: material.alphaMode !== 'BLEND',\n depthTest: material.depthTest !== false,\n terrain: material.terrain,\n nightLight: Boolean(material.nightLight),\n nightSparkle: sparkle,\n uniforms: {\n baseColor: { value: material.color || [1, 1, 1] },\n metallic: { value: material.metallic ?? 0 },\n roughness: { value: material.roughness ?? 0.85 },\n opacity: { value: material.opacity ?? 1 },\n alphaCutoff: { value: material.alphaCutoff ?? 0.5 },\n occlusionStrength: { value: material.occlusionStrength ?? 1 },\n emissive: { value: material.emissive || [0, 0, 0] },\n ...(material.baseTexture ? { baseTexture: { value: material.baseTexture } } : {}),\n ...(material.metalTexture ? { metalTexture: { value: material.metalTexture } } : {}),\n ...(material.emissiveTexture ? { emissiveTexture: { value: material.emissiveTexture } } : {}),\n ...(material.occlusionTexture ? { occlusionTexture: { value: material.occlusionTexture } } : {}),\n ...(material.roofTexture ? { roofTexture: { value: null } } : {}),\n },\n _layerMaterial: material,\n };\n}\n\nexport function removeProgram(program) {\n program.pipelineCache.clear();\n}\n\n/**\n * Add a non-indexed, vertex-coloured mesh from flat vec3 arrays `{ position, normal, color }` in layer-local metres.\n * `usage` adds GPUBufferUsage flags to the vertex buffers (e.g. STORAGE for compute passes).\n */\nexport function createMesh(backend, data, material = {}, usage = 0) {\n const { device } = backend.renderer, attributes = {};\n for (const [name, values] of Object.entries(data)) {\n const array = new Float32Array(values);\n const buffer = device.createBuffer({ size: Math.max(4, array.byteLength), usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST | usage });\n device.queue.writeBuffer(buffer, 0, array);\n attributes[name] = { size: 3, data: array, count: array.length / 3, buffer };\n }\n const mesh = {\n ...emptyNode(),\n device,\n geometry: { device, attributes, drawRange: { start: 0, count: attributes.position.count } },\n program: backend._createProgram({ vertexColors: true, doubleSided: true, ...material }),\n id: L.Layer.types['3d']._nextId++, renderOrder: 0, extras: {},\n };\n backend.add(mesh);\n return mesh;\n}\n\nexport function disposeMesh(backend, mesh) {\n if (!mesh) return;\n backend.remove(mesh);\n for (const attribute of Object.values(mesh.geometry.attributes)) attribute.buffer.destroy();\n backend._removeProgram(mesh.program);\n}\n\n/** Upload retained CPU data and draw one mesh in the active WebGPU render pass. */\nexport function drawGeometry(backend, mesh, pass, cullMode, frontFace) {\n const { geometry, program } = mesh;\n const device = backend.renderer.device;\n const pipelineKey = `${cullMode}:${frontFace}`;\n let pipeline = program.pipelineCache.get(pipelineKey);\n if (!pipeline) {\n pipeline = device.createRenderPipeline({\n layout: program.pipelineLayout,\n vertex: {\n module: program.vertex,\n entryPoint: 'vertexMain',\n buffers: program.vertexBuffers,\n },\n fragment: {\n module: program.fragment,\n entryPoint: 'fragmentMain',\n targets: [{\n format: backend.renderer.format,\n blend: program.transparent ? {\n color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },\n alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },\n } : undefined,\n writeMask: GPUColorWrite.ALL,\n }],\n },\n primitive: { topology: 'triangle-list', cullMode, frontFace },\n depthStencil: {\n format: 'depth24plus',\n depthWriteEnabled: program.depthWrite,\n depthCompare: program.depthTest === false ? 'always' : 'less-equal',\n },\n });\n program.pipelineCache.set(pipelineKey, pipeline);\n }\n\n const uniforms = program.uniforms;\n const renderer = backend.renderer;\n const offset = renderer.uniformSlot++ * 256;\n const data = renderer.uniformData, base = offset / 4;\n data.set(mesh.worldMatrix, base);\n data.set(uniforms.baseColor.value, base + 16);\n data[base + 19] = 1;\n data[base + 20] = uniforms.metallic.value;\n data[base + 21] = uniforms.roughness.value;\n data[base + 22] = uniforms.opacity.value;\n data[base + 23] = uniforms.alphaCutoff.value;\n data.set(uniforms.emissive.value, base + 24);\n data[base + 27] = uniforms.occlusionStrength.value;\n data[base + 28] = mesh.terrain?.[0] ?? 0;\n data[base + 29] = mesh.terrain?.[1] ?? 0;\n data[base + 30] = mesh.terrain?.[2] ?? 0;\n data[base + 31] = mesh.terrain?.[3] ?? 0;\n data[base + 32] = mesh.highlight?.[0] ?? 0;\n data[base + 33] = mesh.highlight?.[1] ?? 0;\n data[base + 34] = program.nightSparkle ? data[55] : 0;\n data[base + 35] = 0;\n\n const textures = mesh._textures ||= [];\n textures.length = 0;\n for (const binding of program.bindings) {\n const texture = uniforms[binding.name]?.value;\n if (!texture) throw new Error(`Missing Layer3D texture: ${binding.name}`);\n if (texture.uploaded !== texture.image) {\n device.queue.copyExternalImageToTexture(\n { source: texture.image, flipY: Boolean(texture.flipY) },\n { texture: texture.texture },\n [texture.image.width, texture.image.height],\n );\n texture.uploaded = texture.image;\n }\n textures.push(texture);\n }\n let bindingsChanged = !mesh.bindGroup || mesh.boundBuffer !== renderer.uniformBuffer ||\n textures.length !== mesh.boundTextures.length;\n for (let i = 0; !bindingsChanged && i < textures.length; i++) {\n if (textures[i].texture !== mesh.boundTextures[i][0] ||\n textures[i].sampler !== mesh.boundTextures[i][1]) bindingsChanged = true;\n }\n if (bindingsChanged) {\n const entries = [\n { binding: 0, resource: { buffer: renderer.uniformBuffer, size: 144 } },\n { binding: 1, resource: { buffer: renderer.uniformBuffer, size: 240 } },\n ];\n if (program.terrain) entries.push({ binding: 2, resource: program.terrain.texture.createView() });\n mesh.boundBuffer = renderer.uniformBuffer;\n const boundTextures = mesh.boundTextures ||= [];\n boundTextures.length = textures.length;\n for (let i = 0; i < textures.length; i++) {\n const binding = program.bindings[i], texture = textures[i];\n entries.push({ binding: binding.texture, resource: texture.texture.createView() });\n entries.push({ binding: binding.sampler, resource: texture.sampler });\n boundTextures[i] ||= [];\n boundTextures[i][0] = texture.texture;\n boundTextures[i][1] = texture.sampler;\n }\n mesh.bindGroup = device.createBindGroup({\n layout: program.bindGroupLayout,\n entries,\n });\n }\n pass.setPipeline(pipeline);\n pass.setBindGroup(0, mesh.bindGroup, [offset]);\n pass.setVertexBuffer(0, geometry.attributes.position.buffer);\n pass.setVertexBuffer(1, geometry.attributes.normal.buffer);\n if (geometry.attributes.color) pass.setVertexBuffer(2, geometry.attributes.color.buffer);\n if (geometry.attributes.uv) pass.setVertexBuffer(3, geometry.attributes.uv.buffer);\n for (const attribute of Object.values(geometry.attributes)) {\n if (!attribute.needsUpdate) continue;\n device.queue.writeBuffer(attribute.buffer, 0, attribute.data);\n attribute.needsUpdate = false;\n geometry.bounds = null;\n }\n const index = geometry.attributes.index;\n if (index) {\n pass.setIndexBuffer(index.buffer, index.indexFormat);\n pass.drawIndexed(geometry.drawRange.count, 1, geometry.drawRange.start, 0, 0);\n } else {\n pass.draw(geometry.drawRange.count, 1, geometry.drawRange.start, 0);\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["materialShaders", "emptyNode", "setParent", "traverse", "attach", "backend", "map", "shared", "rendererReady", "fail", "error", "canvas", "adapter", "device", "event", "owner", "layer", "info", "context", "format", "emptyImage", "pane", "origin", "point", "metres", "object", "geometry", "material", "attributes", "name", "attribute", "data", "size", "count", "normalized", "key", "texture", "destroyTexture", "createProgram", "sparkle", "shaders", "bindings", "textured", "hasTerrain", "vertex", "fragment", "infos", "errors", "message", "vertexBuffers", "bindGroupEntries", "bindGroupLayout", "pipelineLayout", "removeProgram", "program", "createMesh", "usage", "values", "array", "buffer", "mesh", "disposeMesh", "drawGeometry", "pass", "cullMode", "frontFace", "pipelineKey", "pipeline", "uniforms", "renderer", "offset", "base", "textures", "binding", "bindingsChanged", "i", "entries", "boundTextures", "index"]
7
+ }
@@ -0,0 +1,9 @@
1
+ import{lightGrid as U,worldLights as P}from"../utils/lighting.js";import{computeBounds as T}from"../utils/geometry.js";import{traverse as L,updateCamera as _}from"../utils/scene.js";function O(o){const t=o.renderer.device,a=t.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT,buffer:{type:"uniform"}},{binding:1,visibility:GPUShaderStage.FRAGMENT,texture:{sampleType:"depth"}},{binding:2,visibility:GPUShaderStage.FRAGMENT,sampler:{type:"comparison"}},{binding:3,visibility:GPUShaderStage.FRAGMENT,buffer:{type:"read-only-storage"}}]}),s=t.createBuffer({size:80,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),d=t.createSampler({compare:"less-equal",minFilter:"linear",magFilter:"linear"}),u=t.createBuffer({size:32,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST});o.renderer.sunlight={layout:a,buffer:s,sampler:d,lights:u,size:0};const r=new Float32Array(20);r.set([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),t.queue.writeBuffer(s,0,r),o._resizeSunlight(1)}function A(o,t){const a=o.renderer.device,s=o.renderer.sunlight;s.size!==t&&(s.texture&&o._destroyTexture(s.texture),s.size=t,s.texture=a.createTexture({size:[t,t],format:"depth32float",usage:GPUTextureUsage.RENDER_ATTACHMENT|GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_SRC}),o._bindSunlight())}function D(o){const t=o.renderer.sunlight;t.bindGroup=o.renderer.device.createBindGroup({layout:t.layout,entries:[{binding:0,resource:{buffer:t.buffer}},{binding:1,resource:t.texture.createView()},{binding:2,resource:t.sampler},{binding:3,resource:{buffer:t.lights}}]})}function z(o){const{device:t,sunlight:a}=o.renderer,s=[];for(const i of o._layers)!i.options.nightLight||!i.root.visible||L(i.root,n=>{if(!n.visible)return!0;n.extras?.lights&&s.push(n)});if(a.lightMeshes?.length===s.length&&s.every((i,n)=>a.lightMeshes[n].mesh===i&&a.lightMeshes[n].lights===i.extras.lights&&i.worldMatrix.every((p,c)=>p===a.lightMeshes[n].matrix[c])))return;const d=s.map(i=>{const n=i._worldLights,p=i.worldMatrix,c=i.extras.lights;if(n?.lights===c&&p.every((S,v)=>S===n.matrix[v]))return n.data;const w=P(c,p);return i._worldLights={lights:c,matrix:[...p],data:w},w}),{data:u,count:r}=U(d,t.limits.maxStorageBufferBindingSize);a.lights.size<u.byteLength&&(o._destroyTexture(a.lights),a.lights=t.createBuffer({size:u.byteLength,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),o._bindSunlight()),t.queue.writeBuffer(a.lights,0,u),a.lightCount=r,a.lightMeshes=s.map(i=>({mesh:i,lights:i.extras.lights,matrix:[...i.worldMatrix]}))}function F(o,t,a,s){const d=o.renderer,u=d.device,r=d.sunlight;if(!a.length||!s||s.altitude<3){if(!r.enabled)return;const e=new Float32Array(20);e.set([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),u.queue.writeBuffer(r.buffer,0,e),r.enabled=!1;return}const i=new Float32Array(20);r.enabled=!0,o._resizeSunlight(Math.min(2048,u.limits.maxTextureDimension2D));const n=[1/0,1/0,1/0],p=[-1/0,-1/0,-1/0];for(const e of a){e.geometry.bounds||T(e.geometry);const f=e.geometry.bounds,g=new DOMMatrix(e.worldMatrix);for(let l=0;l<8;l++){const h=new DOMPoint(...n.map((y,M)=>l&1<<M?f.max[M]:f.min[M]),1).matrixTransform(g);for(let y=0;y<3;y++){const M=[h.x,h.y,h.z][y]/h.w;n[y]=Math.min(n[y],M),p[y]=Math.max(p[y],M)}}}n[1]=Math.min(0,n[1]);const c=Math.max(100,Math.hypot(...n.map((e,f)=>p[f]-e))),w=n.map((e,f)=>(e+p[f])/2),S={position:w.map((e,f)=>e+s.direction[f]*c*2),target:w,up:[0,0,-1],fov:45,aspect:1,near:1,far:c*5,viewMatrix:[],projectionMatrix:[],projectionViewMatrix:[]};_(S);const v=new DOMMatrix(S.viewMatrix),B=[1/0,1/0,1/0],x=[-1/0,-1/0,-1/0];for(let e=0;e<8;e++){const f=new DOMPoint(...n.map((g,l)=>e&1<<l?p[l]:g),1).matrixTransform(v);[f.x,f.y,f.z].forEach((g,l)=>{B[l]=Math.min(B[l],g-20),x[l]=Math.max(x[l],g+20)})}const b=B.map((e,f)=>x[f]-e),G=new DOMMatrix([2/b[0],0,0,0,0,2/b[1],0,0,0,0,-1/b[2],0,-(x[0]+B[0])/b[0],-(x[1]+B[1])/b[1],x[2]/b[2],1]);if(i.set(G.multiply(v).toFloat32Array()),i[16]=1,u.queue.writeBuffer(r.buffer,0,i),!r.pipeline){r.meshLayout=u.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.VERTEX,buffer:{type:"uniform",hasDynamicOffset:!0}}]}),r.depthLayout=u.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.VERTEX,buffer:{type:"uniform"}}]}),r.depthBindGroup=u.createBindGroup({layout:r.depthLayout,entries:[{binding:0,resource:{buffer:r.buffer}}]});const e=u.createShaderModule({code:`
2
+ struct Mesh { world: mat4x4<f32> };
3
+ @group(0) @binding(0) var<uniform> mesh: Mesh;
4
+ @group(1) @binding(0) var<uniform> matrix: mat4x4<f32>;
5
+ @vertex fn main(@location(0) position: vec3<f32>) -> @builtin(position) vec4<f32> {
6
+ return matrix * mesh.world * vec4<f32>(position, 1.0);
7
+ }
8
+ `});r.pipeline=u.createRenderPipeline({layout:u.createPipelineLayout({bindGroupLayouts:[r.meshLayout,r.depthLayout]}),vertex:{module:e,entryPoint:"main",buffers:[{arrayStride:12,attributes:[{shaderLocation:0,offset:0,format:"float32x3"}]}]},primitive:{topology:"triangle-list",cullMode:"none"},depthStencil:{format:"depth32float",depthWriteEnabled:!0,depthCompare:"less",depthBias:2,depthBiasSlopeScale:2}})}const m=t.beginRenderPass({colorAttachments:[],depthStencilAttachment:{view:r.texture.createView(),depthClearValue:1,depthLoadOp:"clear",depthStoreOp:"store"}});m.setPipeline(r.pipeline),m.setBindGroup(1,r.depthBindGroup);for(const e of a){const f=d.uniformSlot++*256;d.uniformData.set(e.worldMatrix,f/4),e._shadowBuffer!==d.uniformBuffer&&(e._shadowBuffer=d.uniformBuffer,e._shadowBindGroup=u.createBindGroup({layout:r.meshLayout,entries:[{binding:0,resource:{buffer:d.uniformBuffer,size:64}}]}));for(const h of Object.values(e.geometry.attributes))h.needsUpdate&&(u.queue.writeBuffer(h.buffer,0,h.data),h.needsUpdate=!1);m.setBindGroup(0,e._shadowBindGroup,[f]),m.setVertexBuffer(0,e.geometry.attributes.position.buffer);const g=e.geometry.attributes.index,l=e.geometry.drawRange;g?(m.setIndexBuffer(g.buffer,g.indexFormat),m.drawIndexed(l.count,1,l.start,0,0)):m.draw(l.count,1,l.start,0)}m.end()}export{D as bindSunlight,O as initSunlight,F as renderSunlight,A as resizeSunlight,z as updatePointLights};
9
+ //# sourceMappingURL=lighting.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/scene/lighting.js"],
4
+ "sourcesContent": ["import { lightGrid, worldLights } from '../utils/lighting.js';\r\nimport { computeBounds } from '../utils/geometry.js';\r\nimport { traverse, updateCamera } from '../utils/scene.js';\r\n\r\n/** Sunlight shadow maps and night point lights. */\r\nexport function initSunlight(backend) {\r\n const device = backend.renderer.device;\r\n const layout = device.createBindGroupLayout({ entries: [\r\n { binding: 0, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: 'uniform' } },\r\n { binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'depth' } },\r\n { binding: 2, visibility: GPUShaderStage.FRAGMENT, sampler: { type: 'comparison' } },\r\n { binding: 3, visibility: GPUShaderStage.FRAGMENT, buffer: { type: 'read-only-storage' } },\r\n ] });\r\n const buffer = device.createBuffer({ size: 80, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });\r\n const sampler = device.createSampler({ compare: 'less-equal', minFilter: 'linear', magFilter: 'linear' });\r\n const lights = device.createBuffer({ size: 32, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });\r\n backend.renderer.sunlight = { layout, buffer, sampler, lights, size: 0 };\r\n const values = new Float32Array(20);\r\n values.set([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);\r\n device.queue.writeBuffer(buffer, 0, values);\r\n backend._resizeSunlight(1);\r\n}\r\n\r\nexport function resizeSunlight(backend, size) {\r\n const device = backend.renderer.device, sun = backend.renderer.sunlight;\r\n if (sun.size === size) return;\r\n if (sun.texture) backend._destroyTexture(sun.texture);\r\n sun.size = size;\r\n sun.texture = device.createTexture({\r\n size: [size, size], format: 'depth32float',\r\n usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC,\r\n });\r\n backend._bindSunlight();\r\n}\r\n\r\nexport function bindSunlight(backend) {\r\n const sun = backend.renderer.sunlight;\r\n sun.bindGroup = backend.renderer.device.createBindGroup({ layout: sun.layout, entries: [\r\n { binding: 0, resource: { buffer: sun.buffer } },\r\n { binding: 1, resource: sun.texture.createView() },\r\n { binding: 2, resource: sun.sampler },\r\n { binding: 3, resource: { buffer: sun.lights } },\r\n ] });\r\n}\r\n\r\n/** A cached spatial grid limits fragment lighting to nearby lamps, without dropping dense cells. */\r\nexport function updatePointLights(backend) {\r\n const { device, sunlight: sun } = backend.renderer;\r\n const meshes = [];\r\n for (const layer of backend._layers) {\r\n if (!layer.options.nightLight || !layer.root.visible) continue;\r\n traverse(layer.root, mesh => {\r\n if (!mesh.visible) return true;\r\n if (mesh.extras?.lights) meshes.push(mesh);\r\n });\r\n }\r\n if (sun.lightMeshes?.length === meshes.length && meshes.every((mesh, i) => sun.lightMeshes[i].mesh === mesh &&\r\n sun.lightMeshes[i].lights === mesh.extras.lights && mesh.worldMatrix.every((v, j) => v === sun.lightMeshes[i].matrix[j]))) return;\r\n // Per mesh, world-space [x, y, z, radius, r, g, b, intensity] per light: re-packed only when its lights or matrix change.\r\n const packed = meshes.map(mesh => {\r\n const cache = mesh._worldLights, m = mesh.worldMatrix, lights = mesh.extras.lights;\r\n if (cache?.lights === lights && m.every((v, j) => v === cache.matrix[j])) return cache.data;\r\n const values = worldLights(lights, m);\r\n mesh._worldLights = { lights, matrix: [...m], data: values };\r\n return values;\r\n });\r\n const { data, count } = lightGrid(packed, device.limits.maxStorageBufferBindingSize);\r\n if (sun.lights.size < data.byteLength) {\r\n backend._destroyTexture(sun.lights);\r\n sun.lights = device.createBuffer({ size: data.byteLength, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });\r\n backend._bindSunlight();\r\n }\r\n device.queue.writeBuffer(sun.lights, 0, data);\r\n sun.lightCount = count;\r\n sun.lightMeshes = meshes.map(mesh => ({ mesh, lights: mesh.extras.lights, matrix: [...mesh.worldMatrix] }));\r\n}\r\n\r\n/** One shared directional depth map. Off-screen but loaded geometry can cast shadows. */\r\nexport function renderSunlight(backend, encoder, meshes, sunlight) {\r\n const renderer = backend.renderer, device = renderer.device, sun = renderer.sunlight;\r\n if (!meshes.length || !sunlight || sunlight.altitude < 3) {\r\n if (!sun.enabled) return;\r\n const values = new Float32Array(20);\r\n values.set([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);\r\n device.queue.writeBuffer(sun.buffer, 0, values);\r\n sun.enabled = false;\r\n return;\r\n }\r\n const values = new Float32Array(20);\r\n sun.enabled = true;\r\n backend._resizeSunlight(Math.min(2048, device.limits.maxTextureDimension2D));\r\n const min = [Infinity, Infinity, Infinity], max = [-Infinity, -Infinity, -Infinity];\r\n for (const mesh of meshes) {\r\n if (!mesh.geometry.bounds) computeBounds(mesh.geometry);\r\n const bounds = mesh.geometry.bounds, matrix = new DOMMatrix(mesh.worldMatrix);\r\n for (let i = 0; i < 8; i++) {\r\n const point = new DOMPoint(...min.map((_, axis) => (i & (1 << axis)) ? bounds.max[axis] : bounds.min[axis]), 1)\r\n .matrixTransform(matrix);\r\n for (let axis = 0; axis < 3; axis++) {\r\n const value = [point.x, point.y, point.z][axis] / point.w;\r\n min[axis] = Math.min(min[axis], value);\r\n max[axis] = Math.max(max[axis], value);\r\n }\r\n }\r\n }\r\n // Include ground receivers below/around the loaded buildings and trees.\r\n min[1] = Math.min(0, min[1]);\r\n const radius = Math.max(100, Math.hypot(...min.map((v, i) => max[i] - v)));\r\n const center = min.map((v, i) => (v + max[i]) / 2);\r\n const lightCamera = {\r\n position: center.map((v, i) => v + sunlight.direction[i] * radius * 2),\r\n target: center, up: [0, 0, -1], fov: 45, aspect: 1, near: 1, far: radius * 5,\r\n viewMatrix: [], projectionMatrix: [], projectionViewMatrix: [],\r\n };\r\n updateCamera(lightCamera);\r\n const view = new DOMMatrix(lightCamera.viewMatrix);\r\n const low = [Infinity, Infinity, Infinity], high = [-Infinity, -Infinity, -Infinity];\r\n for (let i = 0; i < 8; i++) {\r\n const point = new DOMPoint(...min.map((v, axis) => (i & (1 << axis)) ? max[axis] : v), 1).matrixTransform(view);\r\n [point.x, point.y, point.z].forEach((value, axis) => {\r\n low[axis] = Math.min(low[axis], value - 20);\r\n high[axis] = Math.max(high[axis], value + 20);\r\n });\r\n }\r\n const span = low.map((v, i) => high[i] - v);\r\n const projection = new DOMMatrix([\r\n 2 / span[0], 0, 0, 0, 0, 2 / span[1], 0, 0, 0, 0, -1 / span[2], 0,\r\n -(high[0] + low[0]) / span[0], -(high[1] + low[1]) / span[1], high[2] / span[2], 1,\r\n ]);\r\n values.set(projection.multiply(view).toFloat32Array());\r\n values[16] = 1;\r\n device.queue.writeBuffer(sun.buffer, 0, values);\r\n if (!sun.pipeline) {\r\n sun.meshLayout = device.createBindGroupLayout({ entries: [\r\n { binding: 0, visibility: GPUShaderStage.VERTEX, buffer: { type: 'uniform', hasDynamicOffset: true } },\r\n ] });\r\n // Do not bind the sampled depth texture while it is a render attachment.\r\n sun.depthLayout = device.createBindGroupLayout({ entries: [\r\n { binding: 0, visibility: GPUShaderStage.VERTEX, buffer: { type: 'uniform' } },\r\n ] });\r\n sun.depthBindGroup = device.createBindGroup({\r\n layout: sun.depthLayout, entries: [{ binding: 0, resource: { buffer: sun.buffer } }],\r\n });\r\n const shader = device.createShaderModule({ code: `\r\n struct Mesh { world: mat4x4<f32> };\r\n @group(0) @binding(0) var<uniform> mesh: Mesh;\r\n @group(1) @binding(0) var<uniform> matrix: mat4x4<f32>;\r\n @vertex fn main(@location(0) position: vec3<f32>) -> @builtin(position) vec4<f32> {\r\n return matrix * mesh.world * vec4<f32>(position, 1.0);\r\n }\r\n ` });\r\n sun.pipeline = device.createRenderPipeline({\r\n layout: device.createPipelineLayout({ bindGroupLayouts: [sun.meshLayout, sun.depthLayout] }),\r\n vertex: { module: shader, entryPoint: 'main', buffers: [\r\n { arrayStride: 12, attributes: [{ shaderLocation: 0, offset: 0, format: 'float32x3' }] },\r\n ] },\r\n primitive: { topology: 'triangle-list', cullMode: 'none' },\r\n depthStencil: { format: 'depth32float', depthWriteEnabled: true, depthCompare: 'less',\r\n depthBias: 2, depthBiasSlopeScale: 2 },\r\n });\r\n }\r\n const pass = encoder.beginRenderPass({\r\n colorAttachments: [],\r\n depthStencilAttachment: { view: sun.texture.createView(), depthClearValue: 1,\r\n depthLoadOp: 'clear', depthStoreOp: 'store' },\r\n });\r\n pass.setPipeline(sun.pipeline);\r\n pass.setBindGroup(1, sun.depthBindGroup);\r\n for (const mesh of meshes) {\r\n const offset = renderer.uniformSlot++ * 256;\r\n renderer.uniformData.set(mesh.worldMatrix, offset / 4);\r\n if (mesh._shadowBuffer !== renderer.uniformBuffer) {\r\n mesh._shadowBuffer = renderer.uniformBuffer;\r\n mesh._shadowBindGroup = device.createBindGroup({ layout: sun.meshLayout, entries: [\r\n { binding: 0, resource: { buffer: renderer.uniformBuffer, size: 64 } },\r\n ] });\r\n }\r\n for (const attribute of Object.values(mesh.geometry.attributes)) {\r\n if (attribute.needsUpdate) {\r\n device.queue.writeBuffer(attribute.buffer, 0, attribute.data);\r\n attribute.needsUpdate = false;\r\n }\r\n }\r\n pass.setBindGroup(0, mesh._shadowBindGroup, [offset]);\r\n pass.setVertexBuffer(0, mesh.geometry.attributes.position.buffer);\r\n const index = mesh.geometry.attributes.index, range = mesh.geometry.drawRange;\r\n if (index) {\r\n pass.setIndexBuffer(index.buffer, index.indexFormat);\r\n pass.drawIndexed(range.count, 1, range.start, 0, 0);\r\n } else pass.draw(range.count, 1, range.start, 0);\r\n }\r\n pass.end();\r\n}\r\n"],
5
+ "mappings": "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",
6
+ "names": ["lightGrid", "worldLights", "computeBounds", "traverse", "updateCamera", "initSunlight", "backend", "device", "layout", "buffer", "sampler", "lights", "values", "resizeSunlight", "size", "sun", "bindSunlight", "updatePointLights", "meshes", "layer", "mesh", "i", "v", "j", "packed", "cache", "m", "data", "count", "renderSunlight", "encoder", "sunlight", "renderer", "min", "max", "bounds", "matrix", "point", "_", "axis", "value", "radius", "center", "lightCamera", "view", "low", "high", "span", "projection", "shader", "pass", "offset", "attribute", "index", "range"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{decodeModel as I,readAccessor as F,nodeMatrix as M,vertexNormals as _,textureSampler as z}from"../utils/model.js";import{emptyNode as v,setParent as x}from"../utils/scene.js";async function D(o){await o._added.promise;const g=new URL(o.modelUrl,document.baseURI).href,T=await fetch(g);if(!T.ok)throw new Error(`Unable to load model: HTTP ${T.status}`);const R=await T.arrayBuffer(),i=I(R),E=new URL(".",g).href;await Promise.all((i.buffers||[]).map(async e=>{if(e.binary)return;const t=await fetch(new URL(e.uri,E).href);if(!t.ok)throw new Error(`Unable to load model buffer: HTTP ${t.status}`);e.binary=await t.arrayBuffer()})),await o._added.promise;const A=await Promise.all((i.images||[]).map(async e=>{let t;if(e.bufferView!==void 0){const s=i.bufferViews[e.bufferView],f=i.buffers[s.buffer].binary;t=new Blob([f.slice(s.byteOffset||0,(s.byteOffset||0)+s.byteLength)],{type:e.mimeType})}else{const s=await fetch(new URL(e.uri,E).href);if(!s.ok)throw new Error(`Unable to load model texture: HTTP ${s.status}`);t=await s.blob()}return createImageBitmap(t,{premultiplyAlpha:"none",colorSpaceConversion:"none"})})),y=new Map,p=(e,t=!1)=>{if(!e)return null;if((e.texCoord||0)!==0||e.extensions)throw new Error("Model textures currently require untransformed TEXCOORD_0 UVs");const s=o.renderer.device,f=`${e.index}:${t}`;if(!y.has(f)){const d=i.textures[e.index],l=i.samplers?.[d.sampler]||{},u=A[d.source],c=t?"rgba8unorm-srgb":"rgba8unorm",r=z(l);y.set(f,{device:s,texture:s.createTexture({size:[u.width,u.height],format:c,usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT}),sampler:s.createSampler(r),samplerOptions:r,format:c,srgb:t,image:u,flipY:!1,uploaded:null})}return y.get(f)},B=(i.materials||[]).map(e=>{const t=e.pbrMetallicRoughness||{};return{color:(t.baseColorFactor||[1,1,1,1]).slice(0,3),opacity:t.baseColorFactor?.[3]??1,metallic:t.metallicFactor??1,roughness:t.roughnessFactor??1,baseTexture:p(t.baseColorTexture,!0),metalTexture:p(t.metallicRoughnessTexture),emissiveTexture:p(e.emissiveTexture,!0),occlusionTexture:p(e.occlusionTexture),emissive:e.emissiveFactor||[0,0,0],occlusionStrength:e.occlusionTexture?.strength??1,alphaMode:e.alphaMode||"OPAQUE",alphaCutoff:e.alphaCutoff??.5,doubleSided:e.doubleSided}});let U;{const e=new Set,t=[],s=d=>{if(e.has(d))throw new Error("Cyclic glTF nodes");const l=i.nodes[d];if(!l)throw new Error("Invalid glTF node");e.add(d);const u={...v(),name:l.name};if(u.matrix=M(l),l.mesh!==void 0)for(const c of i.meshes[l.mesh].primitives){const r={};for(const[a,n]of[["POSITION","position"],["NORMAL","normal"],["TEXCOORD_0","uv"]])c.attributes[a]!==void 0&&(r[n]=F(i,c.attributes[a]));if(!r.position)throw new Error("glTF mesh has no positions");const h=r.position.data.length/3;if(r.position.size!==3||h<3||Object.values(r).some(a=>a.data.length/a.size!==h))throw new Error("Invalid glTF vertex attributes");if(c.indices!==void 0){const a=F(i,c.indices);if(r.index={size:1,data:a.indices},![Uint8Array,Uint16Array,Uint32Array].some(n=>a.indices instanceof n)||a.size!==1||a.indices.length%3||[...a.indices].some(n=>n>=h))throw new Error("Invalid glTF triangle indices")}if(!r.index&&h%3)throw new Error("Incomplete glTF triangle");if(!r.normal){const a=_(r.position.data,r.index?.data);r.normal={size:3,data:a}}const m=B[c.material]||{color:[1,1,1],metallic:1};if([m.baseTexture,m.metalTexture,m.emissiveTexture,m.occlusionTexture].some(Boolean)&&!r.uv)throw new Error("Textured model mesh requires UV coordinates");const O=o._createProgram(m);t.push(O);const w=o.renderer.device;r.index?.data instanceof Uint8Array&&(r.index.data=Uint16Array.from(r.index.data));for(const[a,n]of Object.entries(r))n.size||=1,n.count=n.data.length/n.size,n.indexFormat=n.data instanceof Uint32Array?"uint32":"uint16",n.buffer=w.createBuffer({size:Math.max(4,n.data.byteLength),usage:a==="index"?GPUBufferUsage.INDEX|GPUBufferUsage.COPY_DST:GPUBufferUsage.VERTEX|GPUBufferUsage.COPY_DST}),w.queue.writeBuffer(n.buffer,0,n.data);const C={device:w,attributes:r,drawRange:{start:0,count:r.index?.count||r.position.count}};t.push(C);const b={...v(),device:w,geometry:C,program:O,id:L.Layer.types["3d"]._nextId++,renderOrder:0};b.name=i.meshes[l.mesh].name,b.extras=l.extras||{},x(b,u)}for(const c of l.children||[])x(s(c),u);return e.delete(d),u},f=i.scenes?.[i.scene||0];if(!f)throw new Error("Missing glTF scene");try{U=f.nodes.map(s)}catch(d){for(const l of t)if(l.attributes)for(const u of Object.values(l.attributes))u.buffer.destroy();else o._removeProgram(l);throw d}}const P={...v(),id:g,extras:i.extras||{}};if(!U.length)throw new Error("Expected a glTF model with a non-empty scene");for(const e of U)x(e,P);if(o.model=P,o.options.rotation){const[e,t=e,s=e]=o.options.rotation;o.model.rotation[0]=e,o.model.rotation[1]=t,o.model.rotation[2]=s}if(o.options.scale){const[e,t=e,s=e]=o.options.scale;o.model.scale[0]=e,o.model.scale[1]=t,o.model.scale[2]=s}x(o.model,o.root)}export{D as loadModel};
2
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/scene/model.js"],
4
+ "sourcesContent": ["import { decodeModel, readAccessor, nodeMatrix, vertexNormals, textureSampler } from '../utils/model.js';\nimport { emptyNode, setParent } from '../utils/scene.js';\n\n/** glTF/GLB model loading. */\n/** Load the configured GLB/glTF (static triangles) under `root`; GPU resources need attachment. */\nexport async function loadModel(backend) {\n await backend._added.promise;\n const url = new URL(backend.modelUrl, document.baseURI).href;\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Unable to load model: HTTP ${response.status}`);\n }\n const data = await response.arrayBuffer();\n const description = decodeModel(data);\n const directory = new URL('.', url).href;\n await Promise.all((description.buffers || []).map(async buffer => {\n if (buffer.binary) {\n return;\n }\n const response = await fetch(new URL(buffer.uri, directory).href);\n if (!response.ok) {\n throw new Error(`Unable to load model buffer: HTTP ${response.status}`);\n }\n buffer.binary = await response.arrayBuffer();\n }));\n await backend._added.promise;\n const images = await Promise.all((description.images || []).map(async image => {\n let blob;\n if (image.bufferView !== undefined) {\n const view = description.bufferViews[image.bufferView];\n const buffer = description.buffers[view.buffer].binary;\n blob = new Blob([buffer.slice(view.byteOffset || 0, (view.byteOffset || 0) + view.byteLength)],\n { type: image.mimeType });\n } else {\n const response = await fetch(new URL(image.uri, directory).href);\n if (!response.ok) throw new Error(`Unable to load model texture: HTTP ${response.status}`);\n blob = await response.blob();\n }\n // Preserve straight alpha and raw samples; GPU sRGB formats do color decoding.\n return createImageBitmap(blob, { premultiplyAlpha: 'none', colorSpaceConversion: 'none' });\n }));\n const textures = new Map();\n const textureFor = (reference, color = false) => {\n if (!reference) return null;\n if ((reference.texCoord || 0) !== 0 || reference.extensions) {\n throw new Error('Model textures currently require untransformed TEXCOORD_0 UVs');\n }\n const device = backend.renderer.device;\n // A glTF image may serve both color and data maps: those cannot share a GPU format.\n const key = `${reference.index}:${color}`;\n if (!textures.has(key)) {\n const texture = description.textures[reference.index];\n const sampler = description.samplers?.[texture.sampler] || {};\n const image = images[texture.source];\n const format = color ? 'rgba8unorm-srgb' : 'rgba8unorm';\n const samplerOptions = textureSampler(sampler);\n textures.set(key, {\n device,\n texture: device.createTexture({\n size: [image.width, image.height],\n format,\n usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,\n }),\n sampler: device.createSampler(samplerOptions),\n samplerOptions,\n format,\n srgb: color,\n image,\n flipY: false,\n uploaded: null,\n });\n }\n return textures.get(key);\n };\n // Convert glTF materials directly to the shader's retained CPU styles.\n const materials = (description.materials || []).map(material => {\n const pbr = material.pbrMetallicRoughness || {};\n return {\n color: (pbr.baseColorFactor || [1, 1, 1, 1]).slice(0, 3),\n opacity: pbr.baseColorFactor?.[3] ?? 1,\n metallic: pbr.metallicFactor ?? 1,\n roughness: pbr.roughnessFactor ?? 1,\n baseTexture: textureFor(pbr.baseColorTexture, true),\n metalTexture: textureFor(pbr.metallicRoughnessTexture),\n emissiveTexture: textureFor(material.emissiveTexture, true),\n occlusionTexture: textureFor(material.occlusionTexture),\n emissive: material.emissiveFactor || [0, 0, 0],\n occlusionStrength: material.occlusionTexture?.strength ?? 1,\n alphaMode: material.alphaMode || 'OPAQUE',\n alphaCutoff: material.alphaCutoff ?? 0.5,\n doubleSided: material.doubleSided,\n };\n });\n let nodes;\n {\n // Track only the recursion path: shared mesh references are valid, cycles are not.\n const active = new Set(), resources = [];\n const node = index => {\n if (active.has(index)) throw new Error('Cyclic glTF nodes');\n const spec = description.nodes[index];\n if (!spec) throw new Error('Invalid glTF node');\n active.add(index);\n const group = {\n ...emptyNode(),\n name: spec.name\n };\n group.matrix = nodeMatrix(spec);\n if (spec.mesh !== undefined) {\n for (const primitive of description.meshes[spec.mesh].primitives) {\n const attributes = {};\n for (const [semantic, name] of [['POSITION', 'position'], ['NORMAL', 'normal'], ['TEXCOORD_0', 'uv']]) {\n if (primitive.attributes[semantic] !== undefined) attributes[name] = readAccessor(description, primitive.attributes[semantic]);\n }\n if (!attributes.position) throw new Error('glTF mesh has no positions');\n const vertexCount = attributes.position.data.length / 3;\n if (attributes.position.size !== 3 || vertexCount < 3 ||\n Object.values(attributes).some(attribute => attribute.data.length / attribute.size !== vertexCount)) throw new Error('Invalid glTF vertex attributes');\n if (primitive.indices !== undefined) {\n const index = readAccessor(description, primitive.indices);\n attributes.index = { size: 1, data: index.indices };\n if (![Uint8Array, Uint16Array, Uint32Array].some(Type => index.indices instanceof Type) ||\n index.size !== 1 || index.indices.length % 3 || [...index.indices].some(i => i >= vertexCount)) throw new Error('Invalid glTF triangle indices');\n }\n if (!attributes.index && vertexCount % 3) throw new Error('Incomplete glTF triangle');\n if (!attributes.normal) {\n const normals = vertexNormals(attributes.position.data, attributes.index?.data);\n attributes.normal = { size: 3, data: normals };\n }\n const style = materials[primitive.material] || { color: [1, 1, 1], metallic: 1 };\n if ([style.baseTexture, style.metalTexture, style.emissiveTexture, style.occlusionTexture].some(Boolean) && !attributes.uv) {\n throw new Error('Textured model mesh requires UV coordinates');\n }\n const program = backend._createProgram(style);\n resources.push(program);\n const device = backend.renderer.device;\n if (attributes.index?.data instanceof Uint8Array) {\n attributes.index.data = Uint16Array.from(attributes.index.data);\n }\n for (const [name, attribute] of Object.entries(attributes)) {\n attribute.size ||= 1;\n attribute.count = attribute.data.length / attribute.size;\n attribute.indexFormat = attribute.data instanceof Uint32Array ? 'uint32' : 'uint16';\n attribute.buffer = device.createBuffer({\n size: Math.max(4, attribute.data.byteLength),\n usage: name === 'index'\n ? GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST\n : GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\n });\n device.queue.writeBuffer(attribute.buffer, 0, attribute.data);\n }\n const geometry = {\n device,\n attributes,\n drawRange: { start: 0, count: attributes.index?.count || attributes.position.count },\n };\n resources.push(geometry);\n const mesh = {\n ...emptyNode(),\n device,\n geometry,\n program,\n id: L.Layer.types['3d']._nextId++,\n renderOrder: 0\n };\n mesh.name = description.meshes[spec.mesh].name; mesh.extras = spec.extras || {};\n setParent(mesh, group);\n }\n }\n for (const child of spec.children || []) setParent(node(child), group);\n active.delete(index);\n return group;\n };\n const scene = description.scenes?.[description.scene || 0];\n if (!scene) throw new Error('Missing glTF scene');\n try {\n nodes = scene.nodes.map(node);\n } catch (error) {\n // Parsing can fail after earlier primitives allocated buffers/programs.\n for (const resource of resources) {\n if (resource.attributes) {\n for (const attribute of Object.values(resource.attributes)) attribute.buffer.destroy();\n } else backend._removeProgram(resource);\n }\n throw error;\n }\n }\n const model = {\n ...emptyNode(),\n id: url,\n extras: description.extras || {},\n };\n if (!nodes.length) throw new Error('Expected a glTF model with a non-empty scene');\n for (const node of nodes) setParent(node, model);\n backend.model = model;\n if (backend.options.rotation) {\n const [x, y = x, z = x] = backend.options.rotation;\n backend.model.rotation[0] = x; backend.model.rotation[1] = y; backend.model.rotation[2] = z;\n }\n if (backend.options.scale) {\n const [x, y = x, z = x] = backend.options.scale;\n backend.model.scale[0] = x; backend.model.scale[1] = y; backend.model.scale[2] = z;\n }\n setParent(backend.model, backend.root);\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["decodeModel", "readAccessor", "nodeMatrix", "vertexNormals", "textureSampler", "emptyNode", "setParent", "loadModel", "backend", "url", "response", "data", "description", "directory", "buffer", "images", "image", "blob", "view", "textures", "textureFor", "reference", "color", "device", "key", "texture", "sampler", "format", "samplerOptions", "materials", "material", "pbr", "nodes", "active", "resources", "node", "index", "spec", "group", "primitive", "attributes", "semantic", "name", "vertexCount", "attribute", "Type", "i", "normals", "style", "program", "geometry", "mesh", "child", "scene", "error", "resource", "model", "x", "y", "z"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{intersectMeshes as p}from"../utils/picking.js";import{traverse as c,updateWorldMatrix as m,updateCamera as y}from"../utils/scene.js";function v(t,e,r=t.root.children,o=!0){if(!t.renderer)return[];const s=t._map.getContainer().getBoundingClientRect();m(t.scene),y(t.camera);const n=new DOMMatrix(t.camera.projectionViewMatrix).inverse();if(![...n.toFloat64Array()].every(Number.isFinite))throw new Error("Singular 3D transform");t.raycaster.origin[0]=t.camera.position[0],t.raycaster.origin[1]=t.camera.position[1],t.raycaster.origin[2]=t.camera.position[2];const l=new DOMPoint((e.clientX-s.left)/s.width*2-1,-((e.clientY-s.top)/s.height)*2+1,0,1).matrixTransform(n),u=[l.x/l.w,l.y/l.w,l.z/l.w];t.raycaster.direction[0]=u[0]-t.raycaster.origin[0],t.raycaster.direction[1]=u[1]-t.raycaster.origin[1],t.raycaster.direction[2]=u[2]-t.raycaster.origin[2];const f=Math.hypot(...t.raycaster.direction)||1;t.raycaster.direction[0]/=f,t.raycaster.direction[1]/=f,t.raycaster.direction[2]/=f;const a=[];for(const h of r)o?c(h,g=>{g.geometry&&g.visible&&a.push(g)}):h.geometry&&h.visible&&a.push(h);return p(t.raycaster,a,t.renderer.uniformData?.[55])}function _(t,e){if(!t.root||!t.camera)return null;for(const r of t.raycast(e,t.root.children,!!t.model)){if(t.model){let i=r.object;for(;i&&i!==t.model;)i=i.parent;if(i===t.model)return{id:t.model.id,properties:t.model.extras,model:t.model,mesh:r.object,point:r.point}}const o=r.object.extras?.featureFaces;if(!o)continue;let s=0,n=o.length-1;for(;s<=n;){const i=s+n>>1,l=o[i];if(r.faceIndex<l.faceStart)n=i-1;else if(r.faceIndex>=l.faceEnd)s=i+1;else return{...l,mesh:r.object,point:r.point}}}return null}function w(t,e){e?.model&&(e=null),!(t.highlightedFeature?.mesh===e?.mesh&&t.highlightedFeature?.vertexStart===e?.vertexStart)&&(t.highlightedFeature&&(t.highlightedFeature.mesh.highlight=null),t.highlightedFeature=e,e&&(e.mesh.highlight=[e.vertexStart,e.vertexEnd]),t.update())}function d(t,e,r){const o=t._tooltip;if(!o)return;if(e){o.replaceChildren(t.options.tooltipContent(e)),o.hidden=!1;const n=t.container.getBoundingClientRect(),i=r.clientX-n.left,l=r.clientY-n.top;o.style.left=`${i+o.offsetWidth+16>n.width?i-o.offsetWidth-12:i+12}px`,o.style.top=`${l+o.offsetHeight+16>n.height?l-o.offsetHeight-12:l+12}px`}else o.hidden=!0;const s=t._shared;s&&([...s._layers].some(i=>i._tooltip&&!i._tooltip.hidden)?(s._cursor===void 0&&(s._cursor=t.container.style.cursor),t.container.style.cursor="pointer"):s._cursor!==void 0&&(t.container.style.cursor=s._cursor,delete s._cursor))}function C(t,e){t.hoveredFeature=t._findFeature(e),t._setHighlight(t.hoveredFeature),t._showFeature(t.hoveredFeature,e),t.fire("featurehover",{feature:t.hoveredFeature,originalEvent:e})}function j(t,e){const r=t._findFeature(e);t.hoveredFeature=r,t._setHighlight(r),t._showFeature(r,e),t.fire("featureclick",{feature:r,originalEvent:e})}function M(t){t.hoveredFeature=null,t._setHighlight(null),t._showFeature(null),t.fire("featurehover",{feature:null})}export{_ as findFeature,j as onClick,M as onPointerLeave,C as onPointerMove,v as raycast,w as setHighlight,d as showFeature};
2
+ //# sourceMappingURL=picking.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/scene/picking.js"],
4
+ "sourcesContent": ["import { intersectMeshes } from '../utils/picking.js';\r\nimport { traverse, updateWorldMatrix, updateCamera } from '../utils/scene.js';\r\n\r\n/** Raycasting, hover/click highlight and tooltips. */\r\n/**\r\n * Finds the objects under a mouse or pointer event, nearest first.\r\n *\r\n * @param {MouseEvent} event\r\n * @param {Object[]} [objects] Defaults to this layer's root children, not all map layers.\r\n * @param {boolean} [recursive] Also test the descendants of `objects`.\r\n * @returns {Object[]} Nearest hit per mesh: object, shared-world point/distance and triangle faceIndex.\r\n */\r\nexport function raycast(backend, event, objects = backend.root.children, recursive = true) {\r\n if (!backend.renderer) {\r\n return [];\r\n }\r\n const rect = backend._map.getContainer().getBoundingClientRect();\r\n updateWorldMatrix(backend.scene);\r\n updateCamera(backend.camera);\r\n // Unproject the mouse (-1..1) into a world-space ray from the camera.\r\n const inverseCamera = new DOMMatrix(backend.camera.projectionViewMatrix).inverse();\r\n const inverseCameraValues = inverseCamera.toFloat64Array();\r\n if (![...inverseCameraValues].every(Number.isFinite)) {\r\n throw new Error('Singular 3D transform');\r\n }\r\n backend.raycaster.origin[0] = backend.camera.position[0];\r\n backend.raycaster.origin[1] = backend.camera.position[1];\r\n backend.raycaster.origin[2] = backend.camera.position[2];\r\n const unprojected = new DOMPoint(\r\n ((event.clientX - rect.left) / rect.width) * 2 - 1,\r\n -((event.clientY - rect.top) / rect.height) * 2 + 1,\r\n 0,\r\n 1\r\n ).matrixTransform(inverseCamera);\r\n const end = [\r\n unprojected.x / unprojected.w,\r\n unprojected.y / unprojected.w,\r\n unprojected.z / unprojected.w\r\n ];\r\n backend.raycaster.direction[0] = end[0] - backend.raycaster.origin[0];\r\n backend.raycaster.direction[1] = end[1] - backend.raycaster.origin[1];\r\n backend.raycaster.direction[2] = end[2] - backend.raycaster.origin[2];\r\n const rayLength = Math.hypot(...backend.raycaster.direction) || 1;\r\n backend.raycaster.direction[0] /= rayLength;\r\n backend.raycaster.direction[1] /= rayLength;\r\n backend.raycaster.direction[2] /= rayLength;\r\n const meshes = [];\r\n for (const object of objects) {\r\n if (recursive) traverse(object, mesh => { if\r\n (mesh.geometry && mesh.visible) meshes.push(mesh);\r\n });\r\n else if (object.geometry && object.visible) meshes.push(object);\r\n }\r\n return intersectMeshes(backend.raycaster, meshes, backend.renderer.uniformData?.[55]);\r\n}\r\n\r\n/**\r\n * Translate a geometry hit into a configured model or batched source feature.\r\n * Sorted half-open face ranges permit binary search without a feature per triangle.\r\n * @returns {PickedFeature|null}\r\n */\r\nexport function findFeature(backend, event) {\r\n if (!backend.root || !backend.camera) return null;\r\n for (const hit of backend.raycast(event, backend.root.children, Boolean(backend.model))) {\r\n if (backend.model) {\r\n let object = hit.object;\r\n while (object && object !== backend.model) object = object.parent;\r\n if (object === backend.model) {\r\n return {\r\n id: backend.model.id, properties: backend.model.extras,\r\n model: backend.model, mesh: hit.object, point: hit.point\r\n };\r\n }\r\n }\r\n const ranges = hit.object.extras?.featureFaces;\r\n if (!ranges) continue;\r\n let low = 0, high = ranges.length - 1;\r\n while (low <= high) {\r\n const middle = (low + high) >> 1;\r\n const range = ranges[middle];\r\n if (hit.faceIndex < range.faceStart) high = middle - 1;\r\n else if (hit.faceIndex >= range.faceEnd) low = middle + 1;\r\n else return { ...range, mesh: hit.object, point: hit.point };\r\n }\r\n }\r\n return null;\r\n}\r\n\r\n/** Highlight a source vertex range in the shader; vertex colors stay untouched. */\r\nexport function setHighlight(backend, feature) {\r\n if (feature?.model) feature = null;\r\n if (backend.highlightedFeature?.mesh === feature?.mesh &&\r\n backend.highlightedFeature?.vertexStart === feature?.vertexStart) return;\r\n if (backend.highlightedFeature) backend.highlightedFeature.mesh.highlight = null;\r\n backend.highlightedFeature = feature;\r\n if (feature) feature.mesh.highlight = [feature.vertexStart, feature.vertexEnd];\r\n backend.update();\r\n}\r\n\r\n/** Update tooltip content/position and arbitrate cursor ownership across shared layers. */\r\nexport function showFeature(backend, feature, event) {\r\n const tooltip = backend._tooltip;\r\n if (!tooltip) return;\r\n if (feature) {\r\n tooltip.replaceChildren(backend.options.tooltipContent(feature));\r\n tooltip.hidden = false;\r\n const rect = backend.container.getBoundingClientRect();\r\n const x = event.clientX - rect.left, y = event.clientY - rect.top;\r\n tooltip.style.left = `${x + tooltip.offsetWidth + 16 > rect.width ? x - tooltip.offsetWidth - 12 : x + 12}px`;\r\n tooltip.style.top = `${y + tooltip.offsetHeight + 16 > rect.height ? y - tooltip.offsetHeight - 12 : y + 12}px`;\r\n } else {\r\n tooltip.hidden = true;\r\n }\r\n const shared = backend._shared;\r\n if (shared) {\r\n const visible = [...shared._layers].some(layer => layer._tooltip && !layer._tooltip.hidden);\r\n if (visible) {\r\n if (shared._cursor === undefined) shared._cursor = backend.container.style.cursor;\r\n backend.container.style.cursor = 'pointer';\r\n } else if (shared._cursor !== undefined) {\r\n backend.container.style.cursor = shared._cursor;\r\n delete shared._cursor;\r\n }\r\n }\r\n}\r\n\r\n/** Hover is transient; featurehover also reports misses with feature:null. */\r\nexport function onPointerMove(backend, event) {\r\n backend.hoveredFeature = backend._findFeature(event);\r\n backend._setHighlight(backend.hoveredFeature);\r\n backend._showFeature(backend.hoveredFeature, event);\r\n backend.fire('featurehover', { feature: backend.hoveredFeature, originalEvent: event });\r\n}\r\n\r\n/** Emit click without moving the map or pinning the tooltip. */\r\nexport function onClick(backend, event) {\r\n const feature = backend._findFeature(event);\r\n backend.hoveredFeature = feature;\r\n backend._setHighlight(feature);\r\n backend._showFeature(feature, event);\r\n backend.fire('featureclick', { feature, originalEvent: event });\r\n}\r\n\r\n/** Clear hover, highlight and tooltip without retaining a selected feature. */\r\nexport function onPointerLeave(backend) {\r\n backend.hoveredFeature = null;\r\n backend._setHighlight(null);\r\n backend._showFeature(null);\r\n backend.fire('featurehover', { feature: null });\r\n}\r\n"],
5
+ "mappings": "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",
6
+ "names": ["intersectMeshes", "traverse", "updateWorldMatrix", "updateCamera", "raycast", "backend", "event", "objects", "recursive", "rect", "inverseCamera", "unprojected", "end", "rayLength", "meshes", "object", "mesh", "findFeature", "hit", "ranges", "low", "high", "middle", "range", "setHighlight", "feature", "showFeature", "tooltip", "x", "y", "shared", "layer", "onPointerMove", "onClick", "onPointerLeave"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{decodeHeader as I,decodeDirectory as T,decompress as m,tileId as p}from"../utils/pmtiles.js";async function P(e,r,s,f){if(f?.throwIfAborted(),!Number.isSafeInteger(r)||!Number.isSafeInteger(s)||r<0||s<=0||!Number.isSafeInteger(r+s))throw new Error("Invalid PMTiles byte range");const i=await fetch(e.url,{headers:{Range:`bytes=${r}-${r+s-1}`},signal:f});if(!i.ok)throw new Error(`Unable to read PMTiles: HTTP ${i.status}`);const a=i.headers.get("ETag");if(e._etag&&a!==e._etag)throw new Error("PMTiles archive changed; recreate the layer");a&&(e._etag=a);let t=await i.arrayBuffer();if(i.status===200)t=t.slice(r,r+s);else if(i.status!==206||!i.headers.get("Content-Range")?.startsWith(`bytes ${r}-${r+s-1}/`))throw new Error("Invalid PMTiles range response");if(t.byteLength!==s)throw new Error("Truncated PMTiles range");return t}function M(e){return e._header||=e._read(0,127).then(I)}async function x(e,r,s,f,i){const a=p(r,s,f);i?.throwIfAborted();const t=await e.getHeader();if(i?.throwIfAborted(),r<t.minZoom||r>t.maxZoom)return;let d=t.rootDirectoryOffset,c=t.rootDirectoryLength;const y=new Set;for(let _=0;_<4;_++){if(y.has(d))throw new Error("Cyclic PMTiles directory");y.add(d);const h=`${d}:${c}`;if(!e._directories.has(h)){e._directories.size>=32&&e._directories.delete(e._directories.keys().next().value);const n=e._read(d,c).then(u=>m(u,t.internalCompression)).then(T).catch(u=>{throw e._directories.get(h)===n&&e._directories.delete(h),u});e._directories.set(h,n)}const w=await e._directories.get(h);i?.throwIfAborted();let g=0,l=w.length-1;for(;g<=l;){const n=g+l>>1;w[n].tileId<=a?g=n+1:l=n-1}const o=w[l];if(!o)return;if(o.runLength){if(a-o.tileId>=o.runLength)return;if(o.offset+o.length>t.tileDataLength)throw new Error("PMTiles tile outside data section");const n=await m(await e._read(t.tileDataOffset+o.offset,o.length,i),t.tileCompression);return i?.throwIfAborted(),{data:n}}if(o.offset+o.length>t.leafDirectoryLength)throw new Error("PMTiles directory outside leaf section");d=t.leafDirectoryOffset+o.offset,c=o.length}throw new Error("PMTiles directory depth exceeds four levels")}export{M as getHeader,x as getZxy,P as read};
2
+ //# sourceMappingURL=pmtiles.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/scene/pmtiles.js"],
4
+ "sourcesContent": ["import { decodeHeader, decodeDirectory, decompress, tileId } from '../utils/pmtiles.js';\r\n\r\n/**\r\n * PMTiles v3 range reader.\r\n * Supports uncompressed/gzip sections. Other compression methods fail explicitly.\r\n * Specification: https://github.com/protomaps/PMTiles/blob/main/spec/v3/spec.md\r\n */\r\n/** Read exactly one archive range; reject changed archives rather than mix revisions. */\r\nexport async function read(backend, offset, length, signal) {\r\n signal?.throwIfAborted();\r\n if (!Number.isSafeInteger(offset) || !Number.isSafeInteger(length) ||\r\n offset < 0 || length <= 0 || !Number.isSafeInteger(offset + length)) {\r\n throw new Error('Invalid PMTiles byte range');\r\n }\r\n const response = await fetch(backend.url, {\r\n headers: { Range: `bytes=${offset}-${offset + length - 1}` }, signal,\r\n });\r\n if (!response.ok) throw new Error(`Unable to read PMTiles: HTTP ${response.status}`);\r\n const etag = response.headers.get('ETag');\r\n if (backend._etag && etag !== backend._etag) {\r\n throw new Error('PMTiles archive changed; recreate the layer');\r\n }\r\n if (etag) {\r\n backend._etag = etag;\r\n }\r\n let data = await response.arrayBuffer();\r\n if (response.status === 200) {\r\n data = data.slice(offset, offset + length);\r\n } else if (response.status !== 206 || !response.headers.get('Content-Range')?.startsWith(`bytes ${offset}-${offset + length - 1}/`)) {\r\n throw new Error('Invalid PMTiles range response');\r\n }\r\n if (data.byteLength !== length) {\r\n throw new Error('Truncated PMTiles range');\r\n }\r\n return data;\r\n}\r\n\r\n/** Cache header initialization; failures remain visible to all callers. */\r\nexport function getHeader(backend) {\r\n return backend._header ||= backend._read(0, 127).then(decodeHeader);\r\n}\r\n\r\n/** Nearest directory entry at/before the Hilbert ID, then follow leaf pointers. */\r\nexport async function getZxy(backend, z, x, y, signal) {\r\n const id = tileId(z, x, y);\r\n signal?.throwIfAborted();\r\n const header = await backend.getHeader();\r\n signal?.throwIfAborted();\r\n if (z < header.minZoom || z > header.maxZoom) return;\r\n let offset = header.rootDirectoryOffset, length = header.rootDirectoryLength;\r\n const visited = new Set();\r\n for (let depth = 0; depth < 4; depth++) {\r\n if (visited.has(offset)) {\r\n throw new Error('Cyclic PMTiles directory');\r\n }\r\n visited.add(offset);\r\n const key = `${offset}:${length}`;\r\n if (!backend._directories.has(key)) {\r\n if (backend._directories.size >= 32) {\r\n backend._directories.delete(backend._directories.keys().next().value);\r\n }\r\n const pending = backend._read(offset, length)\r\n .then(data => decompress(data, header.internalCompression))\r\n .then(decodeDirectory)\r\n .catch(error => {\r\n if (backend._directories.get(key) === pending) backend._directories.delete(key);\r\n throw error;\r\n });\r\n backend._directories.set(key, pending);\r\n }\r\n const entries = await backend._directories.get(key);\r\n signal?.throwIfAborted();\r\n let low = 0,\r\n high = entries.length - 1;\r\n while (low <= high) {\r\n const mid = (low + high) >> 1;\r\n if (entries[mid].tileId <= id) low = mid + 1;\r\n else high = mid - 1;\r\n }\r\n const entry = entries[high];\r\n if (!entry) return;\r\n if (entry.runLength) {\r\n if (id - entry.tileId >= entry.runLength) return;\r\n if (entry.offset + entry.length > header.tileDataLength) throw new Error('PMTiles tile outside data section');\r\n const data = await decompress(await backend._read(header.tileDataOffset + entry.offset, entry.length, signal), header.tileCompression);\r\n signal?.throwIfAborted();\r\n return { data };\r\n }\r\n if (entry.offset + entry.length > header.leafDirectoryLength) throw new Error('PMTiles directory outside leaf section');\r\n offset = header.leafDirectoryOffset + entry.offset;\r\n length = entry.length;\r\n }\r\n throw new Error('PMTiles directory depth exceeds four levels');\r\n}\r\n"],
5
+ "mappings": "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",
6
+ "names": ["decodeHeader", "decodeDirectory", "decompress", "tileId", "read", "backend", "offset", "length", "signal", "response", "etag", "data", "getHeader", "getZxy", "z", "x", "y", "id", "header", "visited", "depth", "key", "pending", "error", "entries", "low", "high", "mid", "entry"]
7
+ }
@@ -0,0 +1,15 @@
1
+ import{computeBounds as ae}from"../utils/geometry.js";import{traverse as K,updateWorldMatrix as fe,updateCamera as ce}from"../utils/scene.js";function ge(e){const s=e._shared;return s?s!==e?(s.update(),e):(e._frame||=requestAnimationFrame(()=>{e._frame=null,e._shared===e&&e.renderer&&e._render()}),e):e}function xe(e){e._queued||(e._queued=!0,queueMicrotask(()=>{e._queued=!1,!(e._shared!==e||!e.renderer)&&(e._map._animatingZoom?e.update():e._render())}))}function he(e){let s;return e._map.eachLayer(x=>{!s&&x instanceof L.TileLayer&&x._levels&&(s=x._levels[x._tileZoom])}),s}function de(e){e._zoomLevel=e._getTileLevel()}function ye(e){for(const s of e._zoomTransitions?.keys()||[])s.finish();e._zoomTransitions=null,e._zoomLevel=null,e.update()}function _e(e){if(cancelAnimationFrame(e._frame),e._frame=null,e._shared!==e||!e.renderer)return;const s=e._map,x=s.getSize();if(!x.x||!x.y)return;if(s._animatingZoom){e._zoomTransitions||=new Map;const t=performance.now();for(const r of s.getContainer().getAnimations({subtree:!0})){if(!(r instanceof CSSTransition)||r.transitionProperty!=="transform"||!r.effect.target.classList.contains("leaflet-zoom-animated")||e._zoomTransitions.has(r))continue;const i=r.currentTime||0;r.pause(),e._zoomTransitions.set(r,{start:t,time:i})}for(const[r,{start:i,time:c}]of e._zoomTransitions)r.currentTime=Math.min(r.effect.getComputedTiming().endTime,c+t-i)}(!e._size||!e._size.equals(x))&&(e.renderer.width=x.x,e.renderer.height=x.y,e.renderer.canvas.width=Math.round(x.x*e.renderer.dpr),e.renderer.canvas.height=Math.round(x.y*e.renderer.dpr),e.renderer.canvas.style.width=`${x.x}px`,e.renderer.canvas.style.height=`${x.y}px`,e.renderer.depthTexture&&e._destroyTexture(e.renderer.depthTexture),e.renderer.depthTexture=e.renderer.device.createTexture({size:[e.renderer.canvas.width,e.renderer.canvas.height],format:"depth24plus",usage:GPUTextureUsage.RENDER_ATTACHMENT}),e._size=x);let F=s.options.crs.scale(s.getZoom())/(4007501668557849e-8*Math.cos(e._sceneOrigin.lat*Math.PI/180)),j=s.project(e._sceneOrigin).subtract(s.getPixelOrigin());const T=e._zoomLevel?.el.isConnected?e._zoomLevel:e._getTileLevel();if(T&&s._animatingZoom&&(e._zoomLevel=T),T){const t=new DOMMatrix(s._animatingZoom?getComputedStyle(T.el).transform:T.el.style.transform),r=s.project(e._sceneOrigin,T.zoom).subtract(T.origin);j=new L.Point(r.x*t.a+t.e,r.y*t.d+t.f),F*=s.getZoomScale(T.zoom,s.getZoom())*t.a}const G=x.y*1.5/F,Q=s.containerPointToLayerPoint(x.divideBy(2)),B=[(Q.x-j.x)/F,s._terrain3D?._getElevation(s.getCenter())??0,(Q.y-j.y)/F],U=s.getPitch()*Math.PI/180,N=s.getBearing()*Math.PI/180,p=e.camera;p.fov=2*Math.atan(1/3)*180/Math.PI,p.aspect=x.x/x.y,p.near=Math.max(.1,G/1e4),p.far=Math.max(1e4,G*10),p.position[0]=B[0]+Math.sin(N)*Math.sin(U)*G,p.position[1]=B[1]+Math.cos(U)*G,p.position[2]=B[2]+Math.cos(N)*Math.sin(U)*G,p.up[0]=-Math.sin(N)*Math.cos(U),p.up[1]=Math.sin(U),p.up[2]=-Math.cos(N)*Math.cos(U),p.target[0]=B[0],p.target[1]=B[1],p.target[2]=B[2],ce(p);const v=s._sunlight,O=new DOMPoint(...v?.direction||[-300,500,300],0).matrixTransform(new DOMMatrix(p.viewMatrix)),q=Math.hypot(O.x,O.y,O.z)||1,ue=[O.x/q,O.y/q,O.z/q],D=new DOMPoint(0,1,0,0).matrixTransform(new DOMMatrix(p.viewMatrix)),$=Math.hypot(D.x,D.y,D.z)||1,le=[D.x/$,D.y/$,D.z/$],o=p.projectionViewMatrix,n=[[o[3]+o[0],o[7]+o[4],o[11]+o[8],o[15]+o[12]],[o[3]-o[0],o[7]-o[4],o[11]-o[8],o[15]-o[12]],[o[3]+o[1],o[7]+o[5],o[11]+o[9],o[15]+o[13]],[o[3]-o[1],o[7]-o[5],o[11]-o[9],o[15]-o[13]],[o[2],o[6],o[10],o[14]],[o[3]-o[2],o[7]-o[6],o[11]-o[10],o[15]-o[14]]],S=[Math.hypot(n[0][0],n[0][1],n[0][2]),Math.hypot(n[1][0],n[1][1],n[1][2]),Math.hypot(n[2][0],n[2][1],n[2][2]),Math.hypot(n[3][0],n[3][1],n[3][2]),Math.hypot(n[4][0],n[4][1],n[4][2]),Math.hypot(n[5][0],n[5][1],n[5][2])];fe(e.scene),K(e.scene,t=>{if(!t.visible)return!0;if(t.geometry){t.geometry.bounds||ae(t.geometry);const r=t.geometry.bounds,i=t.worldMatrix,c=r.center,a=i[0]*c[0]+i[4]*c[1]+i[8]*c[2]+i[12],l=i[1]*c[0]+i[5]*c[1]+i[9]*c[2]+i[13],f=i[2]*c[0]+i[6]*c[1]+i[10]*c[2]+i[14],m=r.radius*Math.max(Math.hypot(i[0],i[1],i[2]),Math.hypot(i[4],i[5],i[6]),Math.hypot(i[8],i[9],i[10]));if(t._visibleInFrame=n[0][0]*a+n[0][1]*l+n[0][2]*f+n[0][3]>=-m*S[0]&&n[1][0]*a+n[1][1]*l+n[1][2]*f+n[1][3]>=-m*S[1]&&n[2][0]*a+n[2][1]*l+n[2][2]*f+n[2][3]>=-m*S[2]&&n[3][0]*a+n[3][1]*l+n[3][2]*f+n[3][3]>=-m*S[3]&&n[4][0]*a+n[4][1]*l+n[4][2]*f+n[4][3]>=-m*S[4]&&n[5][0]*a+n[5][1]*l+n[5][2]*f+n[5][3]>=-m*S[5],!t._visibleInFrame)return}if(t.program?.transparent){t.geometry.bounds||ae(t.geometry);const r=t.geometry.bounds.center,i=t.worldMatrix,c=i[0]*r[0]+i[4]*r[1]+i[8]*r[2]+i[12],a=i[1]*r[0]+i[5]*r[1]+i[9]*r[2]+i[13],l=i[2]*r[0]+i[6]*r[1]+i[10]*r[2]+i[14],f=p.projectionViewMatrix,m=f[2]*c+f[6]*a+f[10]*l+f[14],b=f[3]*c+f[7]*a+f[11]*l+f[15];t._sortDepth=m/b}});const u=e.renderer;if([...e._layers].some(t=>t.options.roofTexture)){const t=Object.values(s._layers).filter(a=>a instanceof L.Layer.types.pmtiles&&a._tiles&&a.options.opacity>0),r=t.flatMap(a=>Object.values(a._tiles).filter(l=>l.el.done&&!l.el.request.signal.aborted&&l.el.isConnected).map(l=>({...l,layer:a}))),i=r.map(({el:a,coords:l,layer:f})=>`${L.Util.stamp(a)}:${l.z}:${l.x}:${l.y}:${f.options.opacity}:${f.options.zIndex}`).join("|"),c=t.map(a=>`${L.Util.stamp(a)}:${a.options.opacity}:${a.options.zIndex}`).join("|");if(!e._roofRaster||e._roofRaster.selection!==c||!t.some(a=>a.isLoading())&&e._roofRaster.signature!==i){const a=r.length?Math.max(...r.map(g=>g.coords.z)):s.getZoom(),l=new L.Bounds;for(const{coords:g,layer:_}of r){const h=_.getTileSize().multiplyBy(s.getZoomScale(a,g.z));l.extend(new L.Point(g.x,g.y).scaleBy(h)),l.extend(new L.Point(g.x+1,g.y+1).scaleBy(h))}const f=l.min||s.project(e._sceneOrigin,a),m=l.isValid()?l.getSize():new L.Point(1,1),k=Math.min(1,4096/Math.max(m.x,m.y)),w=Math.max(1,Math.ceil(m.x*k)),z=Math.max(1,Math.ceil(m.y*k)),ee=s.project(e._sceneOrigin,a),te=s.options.crs.scale(a)/(4007501668557849e-8*Math.cos(e._sceneOrigin.lat*Math.PI/180)),E=new DOMMatrix;E.m11=te/m.x,E.m33=te/m.y,E.m41=(ee.x-f.x)/m.x,E.m43=(ee.y-f.y)/m.y;const P=e._roofRaster?.texture;P&&(P.width!==w||P.height!==z)&&(e._destroyTexture(P.texture),P.texture=u.device.createTexture({size:[w,z],format:"rgba8unorm",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT}),P.width=w,P.height=z),e._roofRaster={signature:i,selection:c,matrix:Array.from(E.toFloat64Array()),texture:P||{device:u.device,texture:u.device.createTexture({size:[w,z],format:"rgba8unorm",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT}),sampler:u.device.createSampler({addressModeU:"clamp-to-edge",addressModeV:"clamp-to-edge"}),samplerOptions:{addressModeU:"clamp-to-edge",addressModeV:"clamp-to-edge"},image:null,width:w,height:z,format:"rgba8unorm",srgb:!1,flipY:!1,uploaded:null}};const y=u.device;if(!u.roofBlit){const g=y.createShaderModule({code:`
2
+ @group(0) @binding(0) var source: texture_2d<f32>;
3
+ @group(0) @binding(1) var sourceSampler: sampler;
4
+ @group(0) @binding(2) var<uniform> opacity: f32;
5
+ struct Varyings { @builtin(position) position: vec4f, @location(0) uv: vec2f };
6
+ @vertex fn vertexMain(@builtin(vertex_index) index: u32) -> Varyings {
7
+ let uv = vec2f(f32(index & 1u), f32(index >> 1u));
8
+ return Varyings(vec4f(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0, 0.0, 1.0), uv);
9
+ }
10
+ @fragment fn fragmentMain(input: Varyings) -> @location(0) vec4f {
11
+ let color = textureSample(source, sourceSampler, input.uv);
12
+ return vec4f(color.rgb, color.a * opacity);
13
+ }
14
+ `});u.roofBlit={pipeline:y.createRenderPipeline({layout:"auto",vertex:{module:g,entryPoint:"vertexMain"},fragment:{module:g,entryPoint:"fragmentMain",targets:[{format:"rgba8unorm",blend:{color:{srcFactor:"src-alpha",dstFactor:"one-minus-src-alpha",operation:"add"},alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha",operation:"add"}}}]},primitive:{topology:"triangle-strip"}}),sampler:y.createSampler({magFilter:"linear",minFilter:"linear"})}}const Z=e._roofTiles||=new Map;for(const[g,_]of Z)(!r.some(h=>h.el===g)||s._terrain3D?._rasterTiles.has(g))&&(_.texture.destroy(),Z.delete(g));const W=new Float32Array(Math.max(1,r.length)*64),X=y.createBuffer({size:W.byteLength,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),re=y.createCommandEncoder(),I=re.beginRenderPass({colorAttachments:[{view:e._roofRaster.texture.texture.createView(),clearValue:{r:0,g:0,b:0,a:0},loadOp:"clear",storeOp:"store"}]});I.setPipeline(u.roofBlit.pipeline),r.sort((g,_)=>(g.layer.options.zIndex||0)-(_.layer.options.zIndex||0)||g.coords.z-_.coords.z);for(const[g,{coords:_,el:h,layer:oe}]of r.entries()){let d=s._terrain3D?._rasterTiles.get(h)?.texture||Z.get(h);d||(d={texture:y.createTexture({size:[h.naturalWidth,h.naturalHeight],format:"rgba8unorm",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT}),image:h,uploaded:null},Z.set(h,d)),d.uploaded!==d.image&&(y.queue.copyExternalImageToTexture({source:h},{texture:d.texture},[d.texture.width,d.texture.height]),d.uploaded=d.image),W[g*64]=oe.options.opacity;const J=oe.getTileSize().multiplyBy(s.getZoomScale(a,_.z)),ie=new L.Point(_.x,_.y).scaleBy(J).subtract(f),ne=ie.x*w/m.x,se=ie.y*z/m.y;I.setViewport(ne,se,Math.min(J.x*w/m.x,w-ne),Math.min(J.y*z/m.y,z-se),0,1),I.setBindGroup(0,y.createBindGroup({layout:u.roofBlit.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:d.texture.createView()},{binding:1,resource:u.roofBlit.sampler},{binding:2,resource:{buffer:X,offset:g*256,size:4}}]})),I.draw(4)}I.end(),y.queue.writeBuffer(X,0,W),y.queue.submit([re.finish()]),X.destroy()}}else if(e._roofRaster){e._destroyTexture(e._roofRaster.texture.texture),e._roofRaster=null;for(const t of e._roofTiles?.values()||[])t.texture.destroy();e._roofTiles=null}const C=[];K(e.scene,t=>{if(!t.visible)return!0;t.geometry&&t._visibleInFrame&&C.push(t)}),C.sort((t,r)=>Number(t.program.transparent)-Number(r.program.transparent)||t.program.transparent&&((t.renderOrder||0)-(r.renderOrder||0)||r._sortDepth-t._sortDepth)||t.id-r.id);const Y=u.device.createCommandEncoder(),H=[],V=[];if(v?.altitude>=3)for(const t of e._layers)t instanceof L.Layer.types.terrain||K(t.root,r=>{if(!r.visible)return!0;r.geometry&&!r.tile&&!r.program.nightLight&&r.program.depthTest!==!1&&V.push(r)});(!u.uniformBuffer||u.uniformBuffer.size<(C.length*2+V.length+1)*256)&&(u.uniformBuffer&&e._destroyTexture(u.uniformBuffer),u.uniformBuffer=u.device.createBuffer({size:Math.max(64,C.length*4+V.length*2)*256,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),u.uniformData=new Float32Array(u.uniformBuffer.size/4));const M=u.uniformData;M.set(p.viewMatrix,0),M.set(p.projectionMatrix,16),M.set(e._roofRaster?.matrix||[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],32),M.set(ue,48),M[51]=v&&v.altitude<=0?0:1,M.set(le,52);const A=v?Math.max(0,Math.min(1,(3-v.altitude)/9)):0;M[55]=A*A*(3-2*A),M.set(v?.tint||[1,1,1],56),M[59]=0,A>0&&e._updatePointLights(),u.uniformSlot=1,e._renderSunlight(Y,V,v);const R=Y.beginRenderPass({colorAttachments:[{view:u.context.getCurrentTexture().createView(),clearValue:{r:0,g:0,b:0,a:0},loadOp:"clear",storeOp:"store"}],depthStencilAttachment:{view:u.depthTexture.createView(),depthClearValue:1,depthLoadOp:"clear",depthStoreOp:"store"}});R.setViewport(0,0,u.canvas.width,u.canvas.height,0,1),R.setBindGroup(1,u.sunlight.bindGroup);for(const t of C){t.tile&&t.tile.style.visibility!=="hidden"&&H.push(t.tile);const r=t.worldMatrix;t.texture&&(t.program.uniforms.baseTexture.value=t.texture,t.program.uniforms.opacity.value=t.opacity);const i=t.program.uniforms;i.roofTexture&&(i.roofTexture.value=e._roofRaster?.texture||u.roofFallbackTexture);const c=r[0]*(r[5]*r[10]-r[6]*r[9])-r[4]*(r[1]*r[10]-r[2]*r[9])+r[8]*(r[1]*r[6]-r[2]*r[5]);if(!Number.isFinite(c)||c===0)throw new Error("Singular 3D transform");const a=c<0?"cw":"ccw",l=t.program;l.transparent&&l._layerMaterial.doubleSided?(e._drawGeometry(t,R,"front",a),e._drawGeometry(t,R,"back",a)):e._drawGeometry(t,R,l.cullMode,a)}R.end(),u.device.queue.writeBuffer(u.uniformBuffer,0,u.uniformData,0,u.uniformSlot*64),u.device.queue.submit([Y.finish()]),H.length&&u.device.queue.onSubmittedWorkDone().then(()=>{for(const t of H)t.isConnected&&t._terrainVisibility!==void 0&&(t.style.visibility="hidden")},t=>{for(const r of e._layers||[])r.fire("error",{error:t})}),s._animatingZoom&&e.update()}export{ye as endZoom,he as getTileLevel,xe as onMapChange,_e as render,de as startZoom,ge as update};
15
+ //# sourceMappingURL=render.js.map