procedural-terrains 0.1.0-alpha.2 → 2.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 (78) hide show
  1. package/README.md +7 -1
  2. package/dist/{CompileMaterial-xRSfAPw7.js → CompileMaterial-DZiWxNZC.js} +2 -2
  3. package/dist/{CompileMaterial-xRSfAPw7.js.map → CompileMaterial-DZiWxNZC.js.map} +1 -1
  4. package/dist/DetailPageCache-C0qhr02H.js +171 -0
  5. package/dist/DetailPageCache-C0qhr02H.js.map +1 -0
  6. package/dist/FiniteTerrain-B5_3y6xX.js +1925 -0
  7. package/dist/FiniteTerrain-B5_3y6xX.js.map +1 -0
  8. package/dist/{InfiniteTerrain-oi0A4Vxw.js → InfiniteTerrain-jeCy6RED.js} +6 -5
  9. package/dist/{InfiniteTerrain-oi0A4Vxw.js.map → InfiniteTerrain-jeCy6RED.js.map} +1 -1
  10. package/dist/{MiniTerrain-BrVyDXJH.js → MiniTerrain-Bb5VYkZU.js} +101 -24
  11. package/dist/MiniTerrain-Bb5VYkZU.js.map +1 -0
  12. package/dist/{MiniWaterMaterial-BHUu65mQ.js → MiniWaterMaterial-DLml0hi6.js} +35 -10
  13. package/dist/{MiniWaterMaterial-BHUu65mQ.js.map → MiniWaterMaterial-DLml0hi6.js.map} +1 -1
  14. package/dist/{PlanetMaterial-C5npG09e.js → PlanetMaterial-Cs-_x6vr.js} +5 -6
  15. package/dist/{PlanetMaterial-C5npG09e.js.map → PlanetMaterial-Cs-_x6vr.js.map} +1 -1
  16. package/dist/{PlanetTerrain-WWz9mLmY.js → PlanetTerrain-Bsfx7YA6.js} +6 -6
  17. package/dist/{PlanetTerrain-WWz9mLmY.js.map → PlanetTerrain-Bsfx7YA6.js.map} +1 -1
  18. package/dist/ProgramHealthGate-DS47bUo1.js +935 -0
  19. package/dist/ProgramHealthGate-DS47bUo1.js.map +1 -0
  20. package/dist/RevisionScope-Dl4yjj-0.js +210 -0
  21. package/dist/RevisionScope-Dl4yjj-0.js.map +1 -0
  22. package/dist/{SurfaceTextureRoles-CchcgOH1.js → SurfaceTextureRoles-tRGhAZXG.js} +4 -4
  23. package/dist/{SurfaceTextureRoles-CchcgOH1.js.map → SurfaceTextureRoles-tRGhAZXG.js.map} +1 -1
  24. package/dist/{TerrainClouds-I0DrVLLm.js → TerrainClouds-DV6ny7P1.js} +56 -39
  25. package/dist/TerrainClouds-DV6ny7P1.js.map +1 -0
  26. package/dist/{TerrainHeightCache-wAUIBZtx.js → TerrainHeightCache-DIZxi9jz.js} +3 -3
  27. package/dist/{TerrainHeightCache-wAUIBZtx.js.map → TerrainHeightCache-DIZxi9jz.js.map} +1 -1
  28. package/dist/TerrainParameters-DMCjnnxz.js +3375 -0
  29. package/dist/TerrainParameters-DMCjnnxz.js.map +1 -0
  30. package/dist/{TerrainSources-DZZliDBJ.js → TerrainSources-Bye63rDk.js} +947 -3
  31. package/dist/TerrainSources-Bye63rDk.js.map +1 -0
  32. package/dist/{TerrainWater-D78XunS8.js → TerrainWater-CX4XdwA0.js} +150 -39
  33. package/dist/TerrainWater-CX4XdwA0.js.map +1 -0
  34. package/dist/{WaterMaterial-Cy4DiYui.js → WaterMaterial-C9h1xm9E.js} +8 -6
  35. package/dist/WaterMaterial-C9h1xm9E.js.map +1 -0
  36. package/dist/assets.js +1 -1
  37. package/dist/{biomeGLSL-CIkhxIlH.js → biomeGLSL-DWGDDBqT.js} +6 -3
  38. package/dist/{biomeGLSL-CIkhxIlH.js.map → biomeGLSL-DWGDDBqT.js.map} +1 -1
  39. package/dist/clouds.js +2 -2
  40. package/dist/documents.js +17 -7
  41. package/dist/documents.js.map +1 -1
  42. package/dist/export.js +3 -4
  43. package/dist/export.js.map +1 -1
  44. package/dist/index.js +1 -1
  45. package/dist/infinite.js +1 -1
  46. package/dist/mini.js +1 -1
  47. package/dist/planet.js +1 -1
  48. package/dist/{planetGLSL-2xas0f3o.js → planetGLSL-D9Zy2IAw.js} +2 -2
  49. package/dist/{planetGLSL-2xas0f3o.js.map → planetGLSL-D9Zy2IAw.js.map} +1 -1
  50. package/dist/real-world.js +1 -1
  51. package/dist/render.js +29 -175
  52. package/dist/render.js.map +1 -1
  53. package/dist/viewer.js +1 -1
  54. package/dist/water.js +2 -2
  55. package/package.json +1 -1
  56. package/types/clouds.d.ts +20 -6
  57. package/types/index.d.ts +65 -1
  58. package/types/render.d.ts +1 -0
  59. package/types/water.d.ts +13 -6
  60. package/dist/CloudSettings-o31yZs9N.js +0 -138
  61. package/dist/CloudSettings-o31yZs9N.js.map +0 -1
  62. package/dist/CloudShadowPass-BtxrgoAJ.js +0 -1267
  63. package/dist/CloudShadowPass-BtxrgoAJ.js.map +0 -1
  64. package/dist/FiniteTerrain-CtkWkoxn.js +0 -4121
  65. package/dist/FiniteTerrain-CtkWkoxn.js.map +0 -1
  66. package/dist/MiniTerrain-BrVyDXJH.js.map +0 -1
  67. package/dist/ProgramHealthGate-CDL2dmsa.js +0 -172
  68. package/dist/ProgramHealthGate-CDL2dmsa.js.map +0 -1
  69. package/dist/RevisionScope-B6OuKn76.js +0 -68
  70. package/dist/RevisionScope-B6OuKn76.js.map +0 -1
  71. package/dist/TerrainClouds-I0DrVLLm.js.map +0 -1
  72. package/dist/TerrainSources-DZZliDBJ.js.map +0 -1
  73. package/dist/TerrainWater-D78XunS8.js.map +0 -1
  74. package/dist/WaterMaterial-Cy4DiYui.js.map +0 -1
  75. package/dist/WaterSettings-CDMgI_U1.js +0 -410
  76. package/dist/WaterSettings-CDMgI_U1.js.map +0 -1
  77. package/dist/terrainSurfaceTextureGLSL-D9BQYXKy.js +0 -947
  78. package/dist/terrainSurfaceTextureGLSL-D9BQYXKy.js.map +0 -1
package/README.md CHANGED
@@ -4,7 +4,13 @@ An ESM library extracted from Terrain Studio 2.0.0. Finite, infinite, planetary
4
4
  and Mini worlds reuse the studio's terrain geometry, noise, shaders and caches.
5
5
  The package has no React, Electron, account, stylesheet or backend dependency.
6
6
  Three.js `~0.185.1` is a peer dependency; `fflate` handles packed materials,
7
- paint documents and ZIP exports. The current version is an alpha.
7
+ paint documents and ZIP exports. The runtime version follows the studio release; this source builds version 2.0.0.
8
+
9
+ ## Feature coverage and independent effects
10
+
11
+ The runtime does not contain every editor tool. See the [feature matrix](https://github.com/ZyFou/ProceduralTerrains/blob/develop/docs/features.md), [water API](https://github.com/ZyFou/ProceduralTerrains/blob/develop/docs/water.md), [cloud API](https://github.com/ZyFou/ProceduralTerrains/blob/develop/docs/clouds.md), and [larger-world guide](https://github.com/ZyFou/ProceduralTerrains/blob/develop/docs/large-worlds.md).
12
+
13
+ Water and clouds can be used around custom Three.js meshes without generating a terrain. Call createWater/createClouds with renderer and world, add each effect.object, pass effects to TerrainRenderPipeline and update them once per frame. Pass terrain instead to attach a layer managed by that terrain. Separate water/cloud/large-world playground templates are available in the studio documentation.
8
14
 
9
15
  ## Install
10
16
 
@@ -1,5 +1,5 @@
1
1
  import * as THREE from "three";
2
- import { v as validatePrograms, p as programHealthError } from "./ProgramHealthGate-CDL2dmsa.js";
2
+ import { v as validatePrograms, p as programHealthError } from "./ProgramHealthGate-DS47bUo1.js";
3
3
  async function compileMaterial(renderer, material, camera, probe = null) {
4
4
  const geometry = probe?.geometry ?? new THREE.PlaneGeometry(2, 2);
5
5
  const mesh = probe?.mesh ?? new THREE.Mesh(geometry, material);
@@ -16,4 +16,4 @@ async function compileMaterial(renderer, material, camera, probe = null) {
16
16
  export {
17
17
  compileMaterial as c
18
18
  };
19
- //# sourceMappingURL=CompileMaterial-xRSfAPw7.js.map
19
+ //# sourceMappingURL=CompileMaterial-DZiWxNZC.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CompileMaterial-xRSfAPw7.js","sources":["../../../src/runtime/resources/CompileMaterial.js"],"sourcesContent":["import * as THREE from 'three';\nimport { validatePrograms, programHealthError } from '../../engine/render/ProgramHealthGate.js';\n\nexport async function compileMaterial(renderer, material, camera, probe = null) {\n const geometry = probe?.geometry ?? new THREE.PlaneGeometry(2, 2);\n const mesh = probe?.mesh ?? new THREE.Mesh(geometry, material);\n const scene = new THREE.Scene();\n scene.add(mesh);\n try {\n await renderer.compileAsync(scene, camera);\n const health = await validatePrograms({ renderer, materials: [material] });\n if (!health.ok) throw programHealthError(health);\n } finally {\n geometry.dispose();\n }\n}\n"],"names":[],"mappings":";;AAGO,eAAe,gBAAgB,UAAU,UAAU,QAAQ,QAAQ,MAAM;AAC9E,QAAM,WAAW,OAAO,YAAY,IAAI,MAAM,cAAc,GAAG,CAAC;AAChE,QAAM,OAAO,OAAO,QAAQ,IAAI,MAAM,KAAK,UAAU,QAAQ;AAC7D,QAAM,QAAQ,IAAI,MAAM,MAAK;AAC7B,QAAM,IAAI,IAAI;AACd,MAAI;AACF,UAAM,SAAS,aAAa,OAAO,MAAM;AACzC,UAAM,SAAS,MAAM,iBAAiB,EAAE,UAAU,WAAW,CAAC,QAAQ,GAAG;AACzE,QAAI,CAAC,OAAO,GAAI,OAAM,mBAAmB,MAAM;AAAA,EACjD,UAAC;AACC,aAAS,QAAO;AAAA,EAClB;AACF;"}
1
+ {"version":3,"file":"CompileMaterial-DZiWxNZC.js","sources":["../../../src/runtime/resources/CompileMaterial.js"],"sourcesContent":["import * as THREE from 'three';\nimport { validatePrograms, programHealthError } from '../../engine/render/ProgramHealthGate.js';\n\nexport async function compileMaterial(renderer, material, camera, probe = null) {\n const geometry = probe?.geometry ?? new THREE.PlaneGeometry(2, 2);\n const mesh = probe?.mesh ?? new THREE.Mesh(geometry, material);\n const scene = new THREE.Scene();\n scene.add(mesh);\n try {\n await renderer.compileAsync(scene, camera);\n const health = await validatePrograms({ renderer, materials: [material] });\n if (!health.ok) throw programHealthError(health);\n } finally {\n geometry.dispose();\n }\n}\n"],"names":[],"mappings":";;AAGO,eAAe,gBAAgB,UAAU,UAAU,QAAQ,QAAQ,MAAM;AAC9E,QAAM,WAAW,OAAO,YAAY,IAAI,MAAM,cAAc,GAAG,CAAC;AAChE,QAAM,OAAO,OAAO,QAAQ,IAAI,MAAM,KAAK,UAAU,QAAQ;AAC7D,QAAM,QAAQ,IAAI,MAAM,MAAK;AAC7B,QAAM,IAAI,IAAI;AACd,MAAI;AACF,UAAM,SAAS,aAAa,OAAO,MAAM;AACzC,UAAM,SAAS,MAAM,iBAAiB,EAAE,UAAU,WAAW,CAAC,QAAQ,GAAG;AACzE,QAAI,CAAC,OAAO,GAAI,OAAM,mBAAmB,MAAM;AAAA,EACjD,UAAC;AACC,aAAS,QAAO;AAAA,EAClB;AACF;"}
@@ -0,0 +1,171 @@
1
+ import * as THREE from "three";
2
+ const DETAIL_PAGE_SIZE = 256;
3
+ const DETAIL_PAGE_SPAN = 128;
4
+ const DETAIL_PAGE_CACHE_LIMIT = 32;
5
+ const GPU_PAGES = 16;
6
+ const modulo = (value, n) => (value % n + n) % n;
7
+ const keyFor = ({ generation, axis, x, y, scale, seedX, seedY }) => `${generation}:${axis}:${x}:${y}:${scale}:${seedX}:${seedY}`;
8
+ class DetailPageCache {
9
+ constructor({ renderer, uniforms, onReady, onError }) {
10
+ this.renderer = renderer;
11
+ this.uniforms = uniforms;
12
+ this.onReady = onReady;
13
+ this.onError = onError;
14
+ this.cpu = /* @__PURE__ */ new Map();
15
+ this.pending = /* @__PURE__ */ new Map();
16
+ this.readyQueue = [];
17
+ this.pageCoords = Array.from({ length: GPU_PAGES }, () => new THREE.Vector3(1e9, 1e9, 0));
18
+ this.uniforms.uDetailPageCoords.value = this.pageCoords;
19
+ this.texture = new THREE.DataArrayTexture(
20
+ new Uint8Array(DETAIL_PAGE_SIZE * DETAIL_PAGE_SIZE * 4 * GPU_PAGES),
21
+ DETAIL_PAGE_SIZE,
22
+ DETAIL_PAGE_SIZE,
23
+ GPU_PAGES
24
+ );
25
+ this.texture.format = THREE.RGBAFormat;
26
+ this.texture.type = THREE.UnsignedByteType;
27
+ this.texture.wrapS = this.texture.wrapT = THREE.ClampToEdgeWrapping;
28
+ this.texture.magFilter = this.texture.minFilter = THREE.LinearFilter;
29
+ this.texture.generateMipmaps = false;
30
+ this.texture.colorSpace = THREE.NoColorSpace;
31
+ this.texture.source.dataReady = false;
32
+ this.texture.needsUpdate = true;
33
+ this.uniforms.uDetailPageArray.value = this.texture;
34
+ this.hasReadyPage = false;
35
+ this.globalBlend = 0;
36
+ this.axis = 0;
37
+ this.signature = "";
38
+ this.disposed = false;
39
+ }
40
+ _worker() {
41
+ if (this.worker) return this.worker;
42
+ this.worker = new Worker(new URL(
43
+ /* @vite-ignore */
44
+ "" + new URL("assets/DetailPageWorker-CqGjR0ER.js", import.meta.url).href,
45
+ import.meta.url
46
+ ), { type: "module" });
47
+ this.worker.onmessage = ({ data }) => {
48
+ const request = this.pending.get(data.id);
49
+ if (!request) return;
50
+ this.pending.delete(data.id);
51
+ if (request.signature !== this.signature || this.disposed) return;
52
+ if (data.error) {
53
+ this.onError?.(new Error(data.error));
54
+ return;
55
+ }
56
+ const { key, x, y, axis } = request;
57
+ this.cpu.delete(key);
58
+ this.cpu.set(key, data.bytes);
59
+ while (this.cpu.size > DETAIL_PAGE_CACHE_LIMIT) this.cpu.delete(this.cpu.keys().next().value);
60
+ if (this.wanted?.has(key)) this.readyQueue.push({ key, x, y, axis, bytes: data.bytes });
61
+ };
62
+ this.worker.onerror = (event) => {
63
+ this.onError?.(new Error(event.message || "Detail preparation worker failed"));
64
+ this.worker?.terminate();
65
+ this.worker = null;
66
+ this.pending.clear();
67
+ };
68
+ return this.worker;
69
+ }
70
+ _reset(signature, axis) {
71
+ this.signature = signature;
72
+ this.axis = axis;
73
+ this.hasReadyPage = false;
74
+ this.globalBlend = 0;
75
+ this.readyQueue.length = 0;
76
+ this.wanted = /* @__PURE__ */ new Set();
77
+ this.pending.clear();
78
+ this.worker?.terminate();
79
+ this.worker = null;
80
+ for (const value of this.pageCoords) value.set(1e9, 1e9, 0);
81
+ this.uniforms.uDetailPageBasis.value = axis;
82
+ this.uniforms.uDetailGlobalBlend.value = 0;
83
+ }
84
+ invalidate() {
85
+ this._reset("", this.axis);
86
+ }
87
+ onContextRestored() {
88
+ this.invalidate();
89
+ this.texture.userData.allocated = false;
90
+ this.texture.source.dataReady = false;
91
+ this.texture.needsUpdate = true;
92
+ }
93
+ update({ camera, mode, generation, scale, seedX, seedY, active, allowUpload = true, dt }) {
94
+ if (this.disposed || !camera) return;
95
+ const position = camera.position;
96
+ const axis = mode === "planet" ? Math.abs(position.x) > Math.abs(position.y) && Math.abs(position.x) > Math.abs(position.z) ? 1 : Math.abs(position.z) > Math.abs(position.y) ? 2 : 0 : 0;
97
+ const signature = `${generation}:${axis}:${scale}:${seedX}:${seedY}`;
98
+ if (signature !== this.signature) this._reset(signature, axis);
99
+ const cx = axis === 1 ? position.y : position.x;
100
+ const cy = axis === 2 ? position.y : position.z;
101
+ const tx = Math.floor(cx / DETAIL_PAGE_SPAN);
102
+ const ty = Math.floor(cy / DETAIL_PAGE_SPAN);
103
+ const candidates = [];
104
+ for (let dy = -1; dy <= 2; dy++) for (let dx = -1; dx <= 2; dx++) {
105
+ const x = tx + dx;
106
+ const y = ty + dy;
107
+ candidates.push({ generation, axis, x, y, scale, seedX, seedY, distance: dx * dx + dy * dy });
108
+ }
109
+ candidates.sort((a, b) => a.distance - b.distance);
110
+ this.wanted = new Set(candidates.map(keyFor));
111
+ if (this.pending.size && ![...this.pending.values()].some((request) => this.wanted.has(request.key))) {
112
+ this.worker?.terminate();
113
+ this.worker = null;
114
+ this.pending.clear();
115
+ }
116
+ this.readyQueue = this.readyQueue.filter((item) => this.wanted.has(item.key));
117
+ for (const candidate of candidates) {
118
+ const key = keyFor(candidate);
119
+ const slot = modulo(candidate.x, 4) + modulo(candidate.y, 4) * 4;
120
+ const page = this.pageCoords[slot];
121
+ if (page.x === candidate.x && page.y === candidate.y) continue;
122
+ if (this.readyQueue.some((item) => item.key === key)) continue;
123
+ if (this.cpu.has(key)) {
124
+ this.readyQueue.push({ ...candidate, key, bytes: this.cpu.get(key) });
125
+ } else if (this.pending.size < 2 && ![...this.pending.values()].some((item) => item.key === key)) {
126
+ const id = `${Date.now()}:${Math.random()}`;
127
+ this.pending.set(id, { ...candidate, key, signature });
128
+ this._worker().postMessage({ id, options: candidate });
129
+ }
130
+ }
131
+ if (allowUpload && this.readyQueue.length) this._uploadOne();
132
+ if (active && this.hasReadyPage) {
133
+ this.globalBlend = Math.min(1, this.globalBlend + Math.max(0, dt) / 0.25);
134
+ this.uniforms.uDetailGlobalBlend.value = this.globalBlend;
135
+ if (this.globalBlend < 1) this.onReady?.(false);
136
+ }
137
+ }
138
+ _uploadOne() {
139
+ const item = this.readyQueue.shift();
140
+ if (!item || item.axis !== this.axis || !this.wanted.has(item.key)) return;
141
+ const slot = modulo(item.x, 4) + modulo(item.y, 4) * 4;
142
+ const offset = slot * DETAIL_PAGE_SIZE * DETAIL_PAGE_SIZE * 4;
143
+ if (!this.texture.userData.allocated) {
144
+ this.renderer.initTexture(this.texture);
145
+ this.texture.userData.allocated = true;
146
+ }
147
+ this.texture.image.data.set(item.bytes, offset);
148
+ this.texture.source.dataReady = true;
149
+ this.texture.addLayerUpdate(slot);
150
+ this.texture.needsUpdate = true;
151
+ this.renderer.initTexture(this.texture);
152
+ this.pageCoords[slot].set(item.x, item.y, 1);
153
+ if (!this.hasReadyPage) {
154
+ this.hasReadyPage = true;
155
+ this.onReady?.(true);
156
+ }
157
+ }
158
+ dispose() {
159
+ this.disposed = true;
160
+ this.worker?.terminate();
161
+ this.worker = null;
162
+ this.pending.clear();
163
+ this.readyQueue.length = 0;
164
+ this.cpu.clear();
165
+ this.texture.dispose();
166
+ }
167
+ }
168
+ export {
169
+ DetailPageCache as D
170
+ };
171
+ //# sourceMappingURL=DetailPageCache-C0qhr02H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DetailPageCache-C0qhr02H.js","sources":["../../../src/engine/terrain/detail/DetailPageBake.js","../../../src/engine/terrain/detail/DetailPageCache.js"],"sourcesContent":["import { rot2, vnoise2 } from '../noise/cpuNoise.js';\n\nexport const DETAIL_PAGE_SIZE = 256;\nexport const DETAIL_PAGE_SPAN = 128;\nexport const DETAIL_PAGE_CACHE_LIMIT = 32;\n\nconst clamp01 = (value) => Math.min(1, Math.max(0, value));\n\n// Matches the planar noise bands in TerrainDetailMaterial. Each page is\n// world-aligned so changing camera position never changes the pattern.\nexport function detailNoise2D(x, y, scale, seedX, seedY) {\n const px = x * Math.max(scale, 0.0001) + seedX * 0.37;\n const py = y * Math.max(scale, 0.0001) + seedY * 0.37;\n const [rx, ry] = rot2(px, py);\n return clamp01(\n vnoise2(px, py) * 0.50\n + vnoise2(rx * 2.73 + 19.7, ry * 2.73 + 41.1) * 0.32\n + vnoise2(rx * 6.10 + 83.2, ry * 6.10 + 11.4) * 0.18,\n );\n}\n\nexport function bakeDetailPage({ x, y, scale, seedX, seedY }) {\n const bytes = new Uint8Array(DETAIL_PAGE_SIZE * DETAIL_PAGE_SIZE * 4);\n for (let py = 0; py < DETAIL_PAGE_SIZE; py++) {\n const wy = (y + (py + 0.5) / DETAIL_PAGE_SIZE) * DETAIL_PAGE_SPAN;\n for (let px = 0; px < DETAIL_PAGE_SIZE; px++) {\n const wx = (x + (px + 0.5) / DETAIL_PAGE_SIZE) * DETAIL_PAGE_SPAN;\n const offset = (py * DETAIL_PAGE_SIZE + px) * 4;\n bytes[offset] = Math.round(detailNoise2D(wx, wy, scale, seedX, seedY) * 255);\n bytes[offset + 1] = Math.round(detailNoise2D(wx + 53, wy + 29, scale * 0.33, seedX, seedY) * 255);\n bytes[offset + 2] = Math.round(detailNoise2D(wx + 11.3, wy + 23.9, scale * 3, seedX, seedY) * 255);\n bytes[offset + 3] = Math.round(detailNoise2D(wx + 127, wy + 211, scale * 0.085, seedX, seedY) * 255);\n }\n }\n return bytes;\n}\n","import * as THREE from 'three';\nimport { DETAIL_PAGE_SIZE, DETAIL_PAGE_SPAN, DETAIL_PAGE_CACHE_LIMIT } from './DetailPageBake.js';\n\nconst GPU_PAGES = 16;\nconst modulo = (value, n) => ((value % n) + n) % n;\nconst keyFor = ({ generation, axis, x, y, scale, seedX, seedY }) =>\n `${generation}:${axis}:${x}:${y}:${scale}:${seedX}:${seedY}`;\n\nexport class DetailPageCache {\n constructor({ renderer, uniforms, onReady, onError }) {\n this.renderer = renderer;\n this.uniforms = uniforms;\n this.onReady = onReady;\n this.onError = onError;\n this.cpu = new Map();\n this.pending = new Map();\n this.readyQueue = [];\n this.pageCoords = Array.from({ length: GPU_PAGES }, () => new THREE.Vector3(1e9, 1e9, 0));\n this.uniforms.uDetailPageCoords.value = this.pageCoords;\n this.texture = new THREE.DataArrayTexture(\n new Uint8Array(DETAIL_PAGE_SIZE * DETAIL_PAGE_SIZE * 4 * GPU_PAGES),\n DETAIL_PAGE_SIZE, DETAIL_PAGE_SIZE, GPU_PAGES,\n );\n this.texture.format = THREE.RGBAFormat;\n this.texture.type = THREE.UnsignedByteType;\n this.texture.wrapS = this.texture.wrapT = THREE.ClampToEdgeWrapping;\n this.texture.magFilter = this.texture.minFilter = THREE.LinearFilter;\n this.texture.generateMipmaps = false;\n this.texture.colorSpace = THREE.NoColorSpace;\n this.texture.source.dataReady = false;\n this.texture.needsUpdate = true;\n this.uniforms.uDetailPageArray.value = this.texture;\n this.hasReadyPage = false;\n this.globalBlend = 0;\n this.axis = 0;\n this.signature = '';\n this.disposed = false;\n }\n\n _worker() {\n if (this.worker) return this.worker;\n this.worker = new Worker(new URL('./DetailPageWorker.js', import.meta.url), { type: 'module' });\n this.worker.onmessage = ({ data }) => {\n const request = this.pending.get(data.id);\n if (!request) return;\n this.pending.delete(data.id);\n if (request.signature !== this.signature || this.disposed) return;\n if (data.error) { this.onError?.(new Error(data.error)); return; }\n const { key, x, y, axis } = request;\n this.cpu.delete(key);\n this.cpu.set(key, data.bytes);\n while (this.cpu.size > DETAIL_PAGE_CACHE_LIMIT) this.cpu.delete(this.cpu.keys().next().value);\n if (this.wanted?.has(key)) this.readyQueue.push({ key, x, y, axis, bytes: data.bytes });\n };\n this.worker.onerror = (event) => {\n this.onError?.(new Error(event.message || 'Detail preparation worker failed'));\n this.worker?.terminate();\n this.worker = null;\n this.pending.clear();\n };\n return this.worker;\n }\n\n _reset(signature, axis) {\n this.signature = signature;\n this.axis = axis;\n this.hasReadyPage = false;\n this.globalBlend = 0;\n this.readyQueue.length = 0;\n this.wanted = new Set();\n this.pending.clear();\n this.worker?.terminate();\n this.worker = null;\n for (const value of this.pageCoords) value.set(1e9, 1e9, 0);\n this.uniforms.uDetailPageBasis.value = axis;\n this.uniforms.uDetailGlobalBlend.value = 0;\n }\n\n invalidate() { this._reset('', this.axis); }\n\n onContextRestored() {\n this.invalidate();\n this.texture.userData.allocated = false;\n this.texture.source.dataReady = false;\n this.texture.needsUpdate = true;\n }\n\n update({ camera, mode, generation, scale, seedX, seedY, active, allowUpload = true, dt }) {\n if (this.disposed || !camera) return;\n const position = camera.position;\n const axis = mode === 'planet'\n ? Math.abs(position.x) > Math.abs(position.y) && Math.abs(position.x) > Math.abs(position.z) ? 1\n : Math.abs(position.z) > Math.abs(position.y) ? 2 : 0\n : 0;\n const signature = `${generation}:${axis}:${scale}:${seedX}:${seedY}`;\n if (signature !== this.signature) this._reset(signature, axis);\n const cx = axis === 1 ? position.y : position.x;\n const cy = axis === 2 ? position.y : position.z;\n const tx = Math.floor(cx / DETAIL_PAGE_SPAN);\n const ty = Math.floor(cy / DETAIL_PAGE_SPAN);\n const candidates = [];\n for (let dy = -1; dy <= 2; dy++) for (let dx = -1; dx <= 2; dx++) {\n const x = tx + dx;\n const y = ty + dy;\n candidates.push({ generation, axis, x, y, scale, seedX, seedY, distance: dx * dx + dy * dy });\n }\n candidates.sort((a, b) => a.distance - b.distance);\n this.wanted = new Set(candidates.map(keyFor));\n if (this.pending.size && ![...this.pending.values()].some((request) => this.wanted.has(request.key))) {\n this.worker?.terminate();\n this.worker = null;\n this.pending.clear();\n }\n this.readyQueue = this.readyQueue.filter((item) => this.wanted.has(item.key));\n for (const candidate of candidates) {\n const key = keyFor(candidate);\n const slot = modulo(candidate.x, 4) + modulo(candidate.y, 4) * 4;\n const page = this.pageCoords[slot];\n if (page.x === candidate.x && page.y === candidate.y) continue;\n if (this.readyQueue.some((item) => item.key === key)) continue;\n if (this.cpu.has(key)) {\n this.readyQueue.push({ ...candidate, key, bytes: this.cpu.get(key) });\n } else if (this.pending.size < 2 && ![...this.pending.values()].some((item) => item.key === key)) {\n const id = `${Date.now()}:${Math.random()}`;\n this.pending.set(id, { ...candidate, key, signature });\n this._worker().postMessage({ id, options: candidate });\n }\n }\n if (allowUpload && this.readyQueue.length) this._uploadOne();\n if (active && this.hasReadyPage) {\n this.globalBlend = Math.min(1, this.globalBlend + Math.max(0, dt) / 0.25);\n this.uniforms.uDetailGlobalBlend.value = this.globalBlend;\n if (this.globalBlend < 1) this.onReady?.(false);\n }\n }\n\n _uploadOne() {\n const item = this.readyQueue.shift();\n if (!item || item.axis !== this.axis || !this.wanted.has(item.key)) return;\n const slot = modulo(item.x, 4) + modulo(item.y, 4) * 4;\n const offset = slot * DETAIL_PAGE_SIZE * DETAIL_PAGE_SIZE * 4;\n if (!this.texture.userData.allocated) {\n this.renderer.initTexture(this.texture);\n this.texture.userData.allocated = true;\n }\n this.texture.image.data.set(item.bytes, offset);\n this.texture.source.dataReady = true;\n this.texture.addLayerUpdate(slot);\n this.texture.needsUpdate = true;\n this.renderer.initTexture(this.texture);\n this.pageCoords[slot].set(item.x, item.y, 1);\n if (!this.hasReadyPage) {\n this.hasReadyPage = true;\n this.onReady?.(true);\n }\n }\n\n dispose() {\n this.disposed = true;\n this.worker?.terminate();\n this.worker = null;\n this.pending.clear();\n this.readyQueue.length = 0;\n this.cpu.clear();\n this.texture.dispose();\n }\n}\n"],"names":[],"mappings":"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"}