@holoscript/engine 6.0.3 → 6.0.4

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 (192) hide show
  1. package/dist/AutoMesher-CK47F6AV.js +17 -0
  2. package/dist/GPUBuffers-2LHBCD7X.js +9 -0
  3. package/dist/WebGPUContext-TNEUYU2Y.js +11 -0
  4. package/dist/animation/index.cjs +38 -38
  5. package/dist/animation/index.d.cts +1 -1
  6. package/dist/animation/index.d.ts +1 -1
  7. package/dist/animation/index.js +1 -1
  8. package/dist/audio/index.cjs +16 -6
  9. package/dist/audio/index.d.cts +1 -1
  10. package/dist/audio/index.d.ts +1 -1
  11. package/dist/audio/index.js +1 -1
  12. package/dist/camera/index.cjs +23 -23
  13. package/dist/camera/index.d.cts +1 -1
  14. package/dist/camera/index.d.ts +1 -1
  15. package/dist/camera/index.js +1 -1
  16. package/dist/character/index.cjs +6 -4
  17. package/dist/character/index.js +1 -1
  18. package/dist/choreography/index.cjs +1194 -0
  19. package/dist/choreography/index.d.cts +687 -0
  20. package/dist/choreography/index.d.ts +687 -0
  21. package/dist/choreography/index.js +1156 -0
  22. package/dist/chunk-2CSNRI2N.js +217 -0
  23. package/dist/chunk-33T2WINR.js +266 -0
  24. package/dist/chunk-35R73OFM.js +1257 -0
  25. package/dist/chunk-4MMDSUNP.js +1256 -0
  26. package/dist/chunk-5V6HOU72.js +319 -0
  27. package/dist/chunk-6QOP6PYF.js +1038 -0
  28. package/dist/chunk-7KMJVHIL.js +8944 -0
  29. package/dist/chunk-7VPUC62U.js +1106 -0
  30. package/dist/chunk-A2Y6RCAT.js +1878 -0
  31. package/dist/chunk-AHM42MK6.js +8944 -0
  32. package/dist/chunk-BL7IDTHE.js +218 -0
  33. package/dist/chunk-CITOMSWL.js +10462 -0
  34. package/dist/chunk-CXDPKW2K.js +8944 -0
  35. package/dist/chunk-CXZPLD4S.js +223 -0
  36. package/dist/chunk-CZYJE7IH.js +5169 -0
  37. package/dist/chunk-D2OP7YC7.js +6325 -0
  38. package/dist/chunk-EDRVQHUU.js +1544 -0
  39. package/dist/chunk-EJSLOOW2.js +3589 -0
  40. package/dist/chunk-F53SFGW5.js +1878 -0
  41. package/dist/chunk-HCFPELPY.js +919 -0
  42. package/dist/chunk-HNEE36PY.js +93 -0
  43. package/dist/chunk-HYXNV36F.js +1256 -0
  44. package/dist/chunk-IB7KHVFY.js +821 -0
  45. package/dist/chunk-IBBO7YYG.js +690 -0
  46. package/dist/chunk-ILIBGINU.js +5470 -0
  47. package/dist/chunk-IS4MHLKN.js +5479 -0
  48. package/dist/chunk-JT2PFKWD.js +5479 -0
  49. package/dist/chunk-K4CUB4NY.js +1038 -0
  50. package/dist/chunk-KATDQXRJ.js +10462 -0
  51. package/dist/chunk-KBQE6ZFJ.js +8944 -0
  52. package/dist/chunk-KBVD5K7E.js +560 -0
  53. package/dist/chunk-KCDPVQRY.js +4088 -0
  54. package/dist/chunk-KN4QJPKN.js +8944 -0
  55. package/dist/chunk-KWJ3ROSI.js +8944 -0
  56. package/dist/chunk-L45VF6DD.js +919 -0
  57. package/dist/chunk-LY4T37YK.js +307 -0
  58. package/dist/chunk-MDN5WZXA.js +1544 -0
  59. package/dist/chunk-MGCDP6VU.js +928 -0
  60. package/dist/chunk-NCX7X6G2.js +8681 -0
  61. package/dist/chunk-OF54BPVD.js +913 -0
  62. package/dist/chunk-OWSN2Q3Q.js +690 -0
  63. package/dist/chunk-PRRB5TTA.js +406 -0
  64. package/dist/chunk-PXWVQF76.js +4086 -0
  65. package/dist/chunk-PYCOIDT2.js +812 -0
  66. package/dist/chunk-PZCSADOV.js +928 -0
  67. package/dist/chunk-Q2XBVS2K.js +1038 -0
  68. package/dist/chunk-QDZRXWN5.js +1776 -0
  69. package/dist/chunk-RNWOZ6WQ.js +913 -0
  70. package/dist/chunk-ROLFT4CJ.js +1693 -0
  71. package/dist/chunk-SLTJRZ2N.js +266 -0
  72. package/dist/chunk-SRUS5XSU.js +4088 -0
  73. package/dist/chunk-TKCA3WZ5.js +5409 -0
  74. package/dist/chunk-TNRMXYI2.js +1650 -0
  75. package/dist/chunk-TQB3GJGM.js +9763 -0
  76. package/dist/chunk-TUFGXG6K.js +510 -0
  77. package/dist/chunk-U6KMTGQJ.js +632 -0
  78. package/dist/chunk-VMGJQST6.js +8681 -0
  79. package/dist/chunk-X4F4TCG4.js +5470 -0
  80. package/dist/chunk-ZIFROE75.js +1544 -0
  81. package/dist/chunk-ZIJQYHSQ.js +1204 -0
  82. package/dist/combat/index.cjs +4 -4
  83. package/dist/combat/index.d.cts +1 -1
  84. package/dist/combat/index.d.ts +1 -1
  85. package/dist/combat/index.js +1 -1
  86. package/dist/ecs/index.cjs +1 -1
  87. package/dist/ecs/index.js +1 -1
  88. package/dist/environment/index.cjs +14 -14
  89. package/dist/environment/index.d.cts +1 -1
  90. package/dist/environment/index.d.ts +1 -1
  91. package/dist/environment/index.js +1 -1
  92. package/dist/gpu/index.cjs +4810 -0
  93. package/dist/gpu/index.js +3714 -0
  94. package/dist/hologram/index.cjs +27 -1
  95. package/dist/hologram/index.js +1 -1
  96. package/dist/index-B2PIsAmR.d.cts +2180 -0
  97. package/dist/index-B2PIsAmR.d.ts +2180 -0
  98. package/dist/index-BHySEPX7.d.cts +2921 -0
  99. package/dist/index-BJV21zuy.d.cts +341 -0
  100. package/dist/index-BJV21zuy.d.ts +341 -0
  101. package/dist/index-BQutTphC.d.cts +790 -0
  102. package/dist/index-ByIq2XrS.d.cts +3910 -0
  103. package/dist/index-BysHjDSO.d.cts +224 -0
  104. package/dist/index-BysHjDSO.d.ts +224 -0
  105. package/dist/index-CKwAJGck.d.ts +455 -0
  106. package/dist/index-CUl3QstQ.d.cts +3006 -0
  107. package/dist/index-CUl3QstQ.d.ts +3006 -0
  108. package/dist/index-CmYtNiI-.d.cts +953 -0
  109. package/dist/index-CmYtNiI-.d.ts +953 -0
  110. package/dist/index-CnRzWxi_.d.cts +522 -0
  111. package/dist/index-CnRzWxi_.d.ts +522 -0
  112. package/dist/index-CwRWbSC7.d.ts +2921 -0
  113. package/dist/index-CxKIBstO.d.ts +790 -0
  114. package/dist/index-DJ6-R8vh.d.cts +455 -0
  115. package/dist/index-DQKisbcI.d.cts +4968 -0
  116. package/dist/index-DQKisbcI.d.ts +4968 -0
  117. package/dist/index-DRT2zJez.d.ts +3910 -0
  118. package/dist/index-DfNLiAka.d.cts +192 -0
  119. package/dist/index-DfNLiAka.d.ts +192 -0
  120. package/dist/index-nMvkoRm8.d.cts +405 -0
  121. package/dist/index-nMvkoRm8.d.ts +405 -0
  122. package/dist/index-s9yOFU37.d.cts +604 -0
  123. package/dist/index-s9yOFU37.d.ts +604 -0
  124. package/dist/index.cjs +22966 -6960
  125. package/dist/index.d.cts +864 -20
  126. package/dist/index.d.ts +864 -20
  127. package/dist/index.js +3062 -48
  128. package/dist/input/index.cjs +1 -1
  129. package/dist/input/index.js +1 -1
  130. package/dist/orbital/index.cjs +3 -3
  131. package/dist/orbital/index.d.cts +1 -1
  132. package/dist/orbital/index.d.ts +1 -1
  133. package/dist/orbital/index.js +1 -1
  134. package/dist/particles/index.cjs +16 -16
  135. package/dist/particles/index.d.cts +1 -1
  136. package/dist/particles/index.d.ts +1 -1
  137. package/dist/particles/index.js +1 -1
  138. package/dist/physics/index.cjs +2377 -21
  139. package/dist/physics/index.d.cts +1 -1
  140. package/dist/physics/index.d.ts +1 -1
  141. package/dist/physics/index.js +35 -1
  142. package/dist/postfx/index.cjs +3491 -0
  143. package/dist/postfx/index.js +93 -0
  144. package/dist/procedural/index.cjs +1 -1
  145. package/dist/procedural/index.js +1 -1
  146. package/dist/puppeteer-5VF6KDVO.js +52197 -0
  147. package/dist/puppeteer-IZVZ3SG4.js +52197 -0
  148. package/dist/rendering/index.cjs +33 -32
  149. package/dist/rendering/index.d.cts +1 -1
  150. package/dist/rendering/index.d.ts +1 -1
  151. package/dist/rendering/index.js +8 -6
  152. package/dist/runtime/index.cjs +23 -13
  153. package/dist/runtime/index.d.cts +1 -1
  154. package/dist/runtime/index.d.ts +1 -1
  155. package/dist/runtime/index.js +8 -6
  156. package/dist/runtime/protocols/index.cjs +349 -0
  157. package/dist/runtime/protocols/index.js +15 -0
  158. package/dist/scene/index.cjs +8 -8
  159. package/dist/scene/index.d.cts +1 -1
  160. package/dist/scene/index.d.ts +1 -1
  161. package/dist/scene/index.js +1 -1
  162. package/dist/shader/index.cjs +3087 -0
  163. package/dist/shader/index.js +3044 -0
  164. package/dist/simulation/index.cjs +10680 -0
  165. package/dist/simulation/index.d.cts +3 -0
  166. package/dist/simulation/index.d.ts +3 -0
  167. package/dist/simulation/index.js +307 -0
  168. package/dist/spatial/index.cjs +2443 -0
  169. package/dist/spatial/index.d.cts +1545 -0
  170. package/dist/spatial/index.d.ts +1545 -0
  171. package/dist/spatial/index.js +2400 -0
  172. package/dist/terrain/index.cjs +1 -1
  173. package/dist/terrain/index.d.cts +1 -1
  174. package/dist/terrain/index.d.ts +1 -1
  175. package/dist/terrain/index.js +1 -1
  176. package/dist/transformers.node-4NKAPD5U.js +45620 -0
  177. package/dist/vm/index.cjs +7 -8
  178. package/dist/vm/index.d.cts +1 -1
  179. package/dist/vm/index.d.ts +1 -1
  180. package/dist/vm/index.js +1 -1
  181. package/dist/vm-bridge/index.cjs +2 -2
  182. package/dist/vm-bridge/index.d.cts +2 -2
  183. package/dist/vm-bridge/index.d.ts +2 -2
  184. package/dist/vm-bridge/index.js +1 -1
  185. package/dist/vr/index.cjs +6 -6
  186. package/dist/vr/index.js +1 -1
  187. package/dist/world/index.cjs +3 -3
  188. package/dist/world/index.d.cts +1 -1
  189. package/dist/world/index.d.ts +1 -1
  190. package/dist/world/index.js +1 -1
  191. package/package.json +53 -21
  192. package/LICENSE +0 -21
@@ -0,0 +1,812 @@
1
+ import {
2
+ __export
3
+ } from "./chunk-AKLW2MUS.js";
4
+
5
+ // src/terrain/index.ts
6
+ var terrain_exports = {};
7
+ __export(terrain_exports, {
8
+ ErosionBrush: () => ErosionBrush,
9
+ ErosionSim: () => ErosionSim,
10
+ TerrainBrush: () => TerrainBrush,
11
+ TerrainLOD: () => TerrainLOD,
12
+ TerrainPaintLayer: () => TerrainPaintLayer,
13
+ TerrainTexturing: () => TerrainTexturing
14
+ });
15
+
16
+ // src/terrain/TerrainBrush.ts
17
+ function computeFalloff(distance, radius, curve) {
18
+ const t = Math.min(distance / radius, 1);
19
+ switch (curve) {
20
+ case "linear":
21
+ return 1 - t;
22
+ case "smooth":
23
+ return 1 - t * t * (3 - 2 * t);
24
+ case "sphere":
25
+ return Math.sqrt(Math.max(0, 1 - t * t));
26
+ case "tip":
27
+ return (1 - t) * (1 - t);
28
+ case "flat":
29
+ return t < 0.8 ? 1 : 1 - (t - 0.8) / 0.2;
30
+ }
31
+ }
32
+ var TerrainBrush = class {
33
+ grid = /* @__PURE__ */ new Map();
34
+ strokes = [];
35
+ undoStack = [];
36
+ config;
37
+ gridSize;
38
+ constructor(gridSize = 64, config) {
39
+ this.gridSize = gridSize;
40
+ this.config = {
41
+ mode: config?.mode ?? "raise",
42
+ radius: config?.radius ?? 5,
43
+ strength: config?.strength ?? 1,
44
+ falloff: config?.falloff ?? "smooth",
45
+ opacity: config?.opacity ?? 1
46
+ };
47
+ for (let x = 0; x < gridSize; x++) {
48
+ for (let z = 0; z < gridSize; z++) {
49
+ this.grid.set(`${x},${z}`, { height: 0, paintLayer: 0, locked: false });
50
+ }
51
+ }
52
+ }
53
+ /**
54
+ * Apply brush at a position
55
+ */
56
+ apply(cx, cz, overrides) {
57
+ const cfg = { ...this.config, ...overrides };
58
+ const snapshot = /* @__PURE__ */ new Map();
59
+ let affected = 0;
60
+ const r = Math.ceil(cfg.radius);
61
+ let avgHeight = 0;
62
+ let avgCount = 0;
63
+ if (cfg.mode === "flatten" || cfg.mode === "smooth") {
64
+ for (let dx = -r; dx <= r; dx++) {
65
+ for (let dz = -r; dz <= r; dz++) {
66
+ const dist = Math.sqrt(dx * dx + dz * dz);
67
+ if (dist > cfg.radius) continue;
68
+ const cell = this.grid.get(`${cx + dx},${cz + dz}`);
69
+ if (cell) {
70
+ avgHeight += cell.height;
71
+ avgCount++;
72
+ }
73
+ }
74
+ }
75
+ if (avgCount > 0) avgHeight /= avgCount;
76
+ }
77
+ for (let dx = -r; dx <= r; dx++) {
78
+ for (let dz = -r; dz <= r; dz++) {
79
+ const dist = Math.sqrt(dx * dx + dz * dz);
80
+ if (dist > cfg.radius) continue;
81
+ const key = `${cx + dx},${cz + dz}`;
82
+ const cell = this.grid.get(key);
83
+ if (!cell || cell.locked) continue;
84
+ const falloff = computeFalloff(dist, cfg.radius, cfg.falloff);
85
+ const intensity = cfg.strength * cfg.opacity * falloff;
86
+ snapshot.set(key, cell.height);
87
+ switch (cfg.mode) {
88
+ case "raise":
89
+ cell.height += intensity;
90
+ break;
91
+ case "lower":
92
+ cell.height -= intensity;
93
+ break;
94
+ case "smooth":
95
+ cell.height += (avgHeight - cell.height) * intensity * 0.5;
96
+ break;
97
+ case "flatten":
98
+ cell.height += (avgHeight - cell.height) * intensity;
99
+ break;
100
+ case "paint":
101
+ break;
102
+ case "erode":
103
+ cell.height += (avgHeight - cell.height) * intensity * 0.3;
104
+ break;
105
+ }
106
+ affected++;
107
+ }
108
+ }
109
+ this.undoStack.push(snapshot);
110
+ this.strokes.push({ x: cx, z: cz, config: cfg, timestamp: Date.now() });
111
+ return affected;
112
+ }
113
+ /**
114
+ * Paint a layer at a position
115
+ */
116
+ paint(cx, cz, layerIndex) {
117
+ const r = Math.ceil(this.config.radius);
118
+ let painted = 0;
119
+ for (let dx = -r; dx <= r; dx++) {
120
+ for (let dz = -r; dz <= r; dz++) {
121
+ const dist = Math.sqrt(dx * dx + dz * dz);
122
+ if (dist > this.config.radius) continue;
123
+ const cell = this.grid.get(`${cx + dx},${cz + dz}`);
124
+ if (cell && !cell.locked) {
125
+ cell.paintLayer = layerIndex;
126
+ painted++;
127
+ }
128
+ }
129
+ }
130
+ return painted;
131
+ }
132
+ /**
133
+ * Undo last stroke
134
+ */
135
+ undo() {
136
+ const snapshot = this.undoStack.pop();
137
+ if (!snapshot) return false;
138
+ for (const [key, height] of snapshot) {
139
+ const cell = this.grid.get(key);
140
+ if (cell) cell.height = height;
141
+ }
142
+ this.strokes.pop();
143
+ return true;
144
+ }
145
+ /**
146
+ * Get height at a grid position
147
+ */
148
+ getHeight(x, z) {
149
+ return this.grid.get(`${x},${z}`)?.height ?? 0;
150
+ }
151
+ /**
152
+ * Get cell data
153
+ */
154
+ getCell(x, z) {
155
+ return this.grid.get(`${x},${z}`);
156
+ }
157
+ /**
158
+ * Lock/unlock cells
159
+ */
160
+ setLocked(x, z, locked) {
161
+ const cell = this.grid.get(`${x},${z}`);
162
+ if (cell) cell.locked = locked;
163
+ }
164
+ /**
165
+ * Get current config
166
+ */
167
+ getConfig() {
168
+ return { ...this.config };
169
+ }
170
+ /**
171
+ * Set config
172
+ */
173
+ setConfig(cfg) {
174
+ Object.assign(this.config, cfg);
175
+ }
176
+ /**
177
+ * Get stroke count
178
+ */
179
+ getStrokeCount() {
180
+ return this.strokes.length;
181
+ }
182
+ /**
183
+ * Get grid size
184
+ */
185
+ getGridSize() {
186
+ return this.gridSize;
187
+ }
188
+ /**
189
+ * Get undo count
190
+ */
191
+ getUndoCount() {
192
+ return this.undoStack.length;
193
+ }
194
+ /**
195
+ * Get height range
196
+ */
197
+ getHeightRange() {
198
+ let min = Infinity, max = -Infinity;
199
+ for (const cell of this.grid.values()) {
200
+ if (cell.height < min) min = cell.height;
201
+ if (cell.height > max) max = cell.height;
202
+ }
203
+ return { min, max };
204
+ }
205
+ };
206
+
207
+ // src/terrain/TerrainLOD.ts
208
+ var _chunkId = 0;
209
+ var TerrainLOD = class {
210
+ config;
211
+ chunks = /* @__PURE__ */ new Map();
212
+ activeChunks = /* @__PURE__ */ new Set();
213
+ constructor(config) {
214
+ this.config = {
215
+ totalSize: 1024,
216
+ maxLOD: 4,
217
+ baseResolution: 64,
218
+ lodDistances: [100, 250, 500, 1e3],
219
+ morphRange: 0.2,
220
+ ...config
221
+ };
222
+ }
223
+ // ---------------------------------------------------------------------------
224
+ // Generation
225
+ // ---------------------------------------------------------------------------
226
+ generateQuadtree(heightSampler) {
227
+ this.chunks.clear();
228
+ this.activeChunks.clear();
229
+ this.subdivide(0, 0, this.config.totalSize, 0, heightSampler);
230
+ }
231
+ subdivide(x, z, size, level, sampler) {
232
+ const resolution = Math.max(4, this.config.baseResolution >> level);
233
+ const heightData = new Float32Array(resolution * resolution);
234
+ for (let row = 0; row < resolution; row++) {
235
+ for (let col = 0; col < resolution; col++) {
236
+ const wx = x + col / (resolution - 1) * size;
237
+ const wz = z + row / (resolution - 1) * size;
238
+ heightData[row * resolution + col] = sampler(wx, wz);
239
+ }
240
+ }
241
+ const chunk = {
242
+ id: `chunk_${_chunkId++}`,
243
+ level,
244
+ x,
245
+ z,
246
+ size,
247
+ resolution,
248
+ heightData,
249
+ morphFactor: 0,
250
+ active: true,
251
+ children: []
252
+ };
253
+ this.chunks.set(chunk.id, chunk);
254
+ if (level < this.config.maxLOD - 1) {
255
+ const half = size / 2;
256
+ chunk.children = [
257
+ this.subdivide(x, z, half, level + 1, sampler),
258
+ this.subdivide(x + half, z, half, level + 1, sampler),
259
+ this.subdivide(x, z + half, half, level + 1, sampler),
260
+ this.subdivide(x + half, z + half, half, level + 1, sampler)
261
+ ];
262
+ }
263
+ return chunk.id;
264
+ }
265
+ // ---------------------------------------------------------------------------
266
+ // LOD Selection
267
+ // ---------------------------------------------------------------------------
268
+ selectLOD(cameraX, cameraZ) {
269
+ this.activeChunks.clear();
270
+ for (const chunk of this.chunks.values()) {
271
+ const cx = chunk.x + chunk.size / 2;
272
+ const cz = chunk.z + chunk.size / 2;
273
+ const dx = cx - cameraX;
274
+ const dz = cz - cameraZ;
275
+ const dist = Math.sqrt(dx * dx + dz * dz);
276
+ const threshold = this.config.lodDistances[chunk.level] ?? Infinity;
277
+ const morphStart = threshold * (1 - this.config.morphRange);
278
+ if (dist < threshold) {
279
+ chunk.active = true;
280
+ chunk.morphFactor = dist > morphStart ? (dist - morphStart) / (threshold - morphStart) : 0;
281
+ this.activeChunks.add(chunk.id);
282
+ } else {
283
+ chunk.active = false;
284
+ chunk.morphFactor = 1;
285
+ }
286
+ }
287
+ }
288
+ // ---------------------------------------------------------------------------
289
+ // Stitching
290
+ // ---------------------------------------------------------------------------
291
+ getStitchEdges(chunkId) {
292
+ const chunk = this.chunks.get(chunkId);
293
+ if (!chunk) return { north: false, south: false, east: false, west: false };
294
+ const result = { north: false, south: false, east: false, west: false };
295
+ for (const other of this.chunks.values()) {
296
+ if (other.id === chunkId || !other.active || other.level === chunk.level) continue;
297
+ if (Math.abs(other.z + other.size - chunk.z) < 0.1 && this.overlapsX(chunk, other))
298
+ result.north = true;
299
+ if (Math.abs(chunk.z + chunk.size - other.z) < 0.1 && this.overlapsX(chunk, other))
300
+ result.south = true;
301
+ if (Math.abs(other.x + other.size - chunk.x) < 0.1 && this.overlapsZ(chunk, other))
302
+ result.west = true;
303
+ if (Math.abs(chunk.x + chunk.size - other.x) < 0.1 && this.overlapsZ(chunk, other))
304
+ result.east = true;
305
+ }
306
+ return result;
307
+ }
308
+ overlapsX(a, b) {
309
+ return a.x < b.x + b.size && a.x + a.size > b.x;
310
+ }
311
+ overlapsZ(a, b) {
312
+ return a.z < b.z + b.size && a.z + a.size > b.z;
313
+ }
314
+ // ---------------------------------------------------------------------------
315
+ // Queries
316
+ // ---------------------------------------------------------------------------
317
+ getChunk(id) {
318
+ return this.chunks.get(id);
319
+ }
320
+ getActiveChunks() {
321
+ return [...this.chunks.values()].filter((c) => c.active);
322
+ }
323
+ getActiveChunkCount() {
324
+ return this.activeChunks.size;
325
+ }
326
+ getTotalChunkCount() {
327
+ return this.chunks.size;
328
+ }
329
+ sampleHeight(x, z) {
330
+ for (const chunk of this.chunks.values()) {
331
+ if (!chunk.active) continue;
332
+ if (x >= chunk.x && x < chunk.x + chunk.size && z >= chunk.z && z < chunk.z + chunk.size) {
333
+ const lx = (x - chunk.x) / chunk.size;
334
+ const lz = (z - chunk.z) / chunk.size;
335
+ const col = Math.min(chunk.resolution - 1, Math.floor(lx * (chunk.resolution - 1)));
336
+ const row = Math.min(chunk.resolution - 1, Math.floor(lz * (chunk.resolution - 1)));
337
+ return chunk.heightData[row * chunk.resolution + col];
338
+ }
339
+ }
340
+ return 0;
341
+ }
342
+ };
343
+
344
+ // src/terrain/TerrainPaintLayer.ts
345
+ var TerrainPaintLayer = class {
346
+ layers = [];
347
+ splatMap = /* @__PURE__ */ new Map();
348
+ gridSize;
349
+ undoStack = [];
350
+ constructor(gridSize = 64) {
351
+ this.gridSize = gridSize;
352
+ }
353
+ /**
354
+ * Add a texture layer
355
+ */
356
+ addLayer(layer) {
357
+ this.layers.push(layer);
358
+ const idx = this.layers.length - 1;
359
+ for (const weights of this.splatMap.values()) {
360
+ weights.weights.push(0);
361
+ }
362
+ if (idx === 0) {
363
+ for (let x = 0; x < this.gridSize; x++) {
364
+ for (let z = 0; z < this.gridSize; z++) {
365
+ this.splatMap.set(`${x},${z}`, { weights: [1] });
366
+ }
367
+ }
368
+ }
369
+ return idx;
370
+ }
371
+ /**
372
+ * Remove a layer by index
373
+ */
374
+ removeLayer(index) {
375
+ if (index < 0 || index >= this.layers.length) return false;
376
+ this.layers.splice(index, 1);
377
+ for (const entry of this.splatMap.values()) {
378
+ entry.weights.splice(index, 1);
379
+ this.normalizeWeights(entry.weights);
380
+ }
381
+ return true;
382
+ }
383
+ /**
384
+ * Paint a layer at a position with given weight
385
+ */
386
+ paintAt(x, z, layerIndex, weight, radius = 1) {
387
+ if (layerIndex < 0 || layerIndex >= this.layers.length) return 0;
388
+ const snapshot = /* @__PURE__ */ new Map();
389
+ let painted = 0;
390
+ const r = Math.ceil(radius);
391
+ for (let dx = -r; dx <= r; dx++) {
392
+ for (let dz = -r; dz <= r; dz++) {
393
+ const dist = Math.sqrt(dx * dx + dz * dz);
394
+ if (dist > radius) continue;
395
+ const key = `${x + dx},${z + dz}`;
396
+ let entry = this.splatMap.get(key);
397
+ if (!entry) {
398
+ entry = { weights: new Array(this.layers.length).fill(0) };
399
+ if (entry.weights.length > 0) entry.weights[0] = 1;
400
+ this.splatMap.set(key, entry);
401
+ }
402
+ snapshot.set(key, [...entry.weights]);
403
+ const falloff = 1 - dist / radius;
404
+ const targetWeight = weight * falloff;
405
+ entry.weights[layerIndex] = Math.min(1, entry.weights[layerIndex] + targetWeight);
406
+ this.normalizeWeights(entry.weights);
407
+ painted++;
408
+ }
409
+ }
410
+ this.undoStack.push(snapshot);
411
+ return painted;
412
+ }
413
+ /**
414
+ * Get weights at a position
415
+ */
416
+ getWeights(x, z) {
417
+ return this.splatMap.get(`${x},${z}`)?.weights ?? [];
418
+ }
419
+ /**
420
+ * Get dominant layer at a position
421
+ */
422
+ getDominantLayer(x, z) {
423
+ const weights = this.getWeights(x, z);
424
+ if (weights.length === 0) return -1;
425
+ let maxIdx = 0;
426
+ for (let i = 1; i < weights.length; i++) {
427
+ if (weights[i] > weights[maxIdx]) maxIdx = i;
428
+ }
429
+ return maxIdx;
430
+ }
431
+ /**
432
+ * Undo last paint operation
433
+ */
434
+ undo() {
435
+ const snapshot = this.undoStack.pop();
436
+ if (!snapshot) return false;
437
+ for (const [key, weights] of snapshot) {
438
+ const entry = this.splatMap.get(key);
439
+ if (entry) entry.weights = weights;
440
+ }
441
+ return true;
442
+ }
443
+ /**
444
+ * Normalize weights to sum to 1
445
+ */
446
+ normalizeWeights(weights) {
447
+ const sum = weights.reduce((a, b) => a + b, 0);
448
+ if (sum > 0) {
449
+ for (let i = 0; i < weights.length; i++) weights[i] /= sum;
450
+ }
451
+ }
452
+ /**
453
+ * Get layer definitions
454
+ */
455
+ getLayers() {
456
+ return [...this.layers];
457
+ }
458
+ getLayerCount() {
459
+ return this.layers.length;
460
+ }
461
+ getUndoCount() {
462
+ return this.undoStack.length;
463
+ }
464
+ };
465
+
466
+ // src/terrain/TerrainTexturing.ts
467
+ var TerrainTexturing = class {
468
+ layers = [];
469
+ splatmap = null;
470
+ triplanar = { sharpness: 4, tiling: 1, enabled: false };
471
+ detailLayers = [];
472
+ // ---------------------------------------------------------------------------
473
+ // Layers
474
+ // ---------------------------------------------------------------------------
475
+ addLayer(layer) {
476
+ if (this.layers.length >= 16) throw new Error("Max 16 terrain layers");
477
+ this.layers.push(layer);
478
+ }
479
+ getLayer(index) {
480
+ return this.layers[index];
481
+ }
482
+ getLayerCount() {
483
+ return this.layers.length;
484
+ }
485
+ removeLayer(id) {
486
+ this.layers = this.layers.filter((l) => l.id !== id);
487
+ }
488
+ // ---------------------------------------------------------------------------
489
+ // Splatmap
490
+ // ---------------------------------------------------------------------------
491
+ createSplatmap(width, height, channelCount = 4) {
492
+ const channels = [];
493
+ for (let i = 0; i < channelCount; i++) {
494
+ const data = new Float32Array(width * height);
495
+ if (i === 0) data.fill(1);
496
+ channels.push(data);
497
+ }
498
+ this.splatmap = { width, height, channels };
499
+ return this.splatmap;
500
+ }
501
+ paintSplatmap(channel, x, z, radius, strength, _heightMap) {
502
+ if (!this.splatmap || channel >= this.splatmap.channels.length) return;
503
+ const w = this.splatmap.width, h = this.splatmap.height;
504
+ const px = Math.floor(x * w), pz = Math.floor(z * h);
505
+ const r = Math.floor(radius * Math.min(w, h));
506
+ for (let dy = -r; dy <= r; dy++) {
507
+ for (let dx = -r; dx <= r; dx++) {
508
+ const sx = px + dx, sz = pz + dy;
509
+ if (sx < 0 || sx >= w || sz < 0 || sz >= h) continue;
510
+ const dist = Math.sqrt(dx * dx + dy * dy) / r;
511
+ if (dist > 1) continue;
512
+ const falloff = 1 - dist * dist;
513
+ const idx = sz * w + sx;
514
+ this.splatmap.channels[channel][idx] = Math.min(
515
+ 1,
516
+ this.splatmap.channels[channel][idx] + strength * falloff
517
+ );
518
+ this.normalizeSplatAt(idx);
519
+ }
520
+ }
521
+ }
522
+ normalizeSplatAt(idx) {
523
+ if (!this.splatmap) return;
524
+ let total = 0;
525
+ for (const ch of this.splatmap.channels) total += ch[idx];
526
+ if (total > 0) {
527
+ for (const ch of this.splatmap.channels) ch[idx] /= total;
528
+ }
529
+ }
530
+ getSplatWeights(x, z) {
531
+ if (!this.splatmap) return [1, 0, 0, 0];
532
+ const w = this.splatmap.width, h = this.splatmap.height;
533
+ const px = Math.min(w - 1, Math.max(0, Math.floor(x * w)));
534
+ const pz = Math.min(h - 1, Math.max(0, Math.floor(z * h)));
535
+ const idx = pz * w + px;
536
+ return this.splatmap.channels.map((ch) => ch[idx]);
537
+ }
538
+ // ---------------------------------------------------------------------------
539
+ // Triplanar
540
+ // ---------------------------------------------------------------------------
541
+ setTriplanar(config) {
542
+ Object.assign(this.triplanar, config);
543
+ }
544
+ getTriplanar() {
545
+ return { ...this.triplanar };
546
+ }
547
+ computeTriplanarWeights(normal) {
548
+ const s = this.triplanar.sharpness;
549
+ const ax = Math.pow(Math.abs(normal.x), s);
550
+ const ay = Math.pow(Math.abs(normal.y), s);
551
+ const az = Math.pow(Math.abs(normal.z), s);
552
+ const total = ax + ay + az || 1;
553
+ return { x: ax / total, y: ay / total, z: az / total };
554
+ }
555
+ // ---------------------------------------------------------------------------
556
+ // Detail Layers
557
+ // ---------------------------------------------------------------------------
558
+ addDetailLayer(layerIndex, distance, tiling) {
559
+ this.detailLayers.push({ layerIndex, distance, tiling });
560
+ }
561
+ getDetailLayers() {
562
+ return [...this.detailLayers];
563
+ }
564
+ };
565
+
566
+ // src/terrain/ErosionBrush.ts
567
+ var ErosionBrush = class {
568
+ heightmap = /* @__PURE__ */ new Map();
569
+ config;
570
+ gridSize;
571
+ constructor(gridSize = 64, config) {
572
+ this.gridSize = gridSize;
573
+ this.config = {
574
+ type: config?.type ?? "hydraulic",
575
+ radius: config?.radius ?? 5,
576
+ strength: config?.strength ?? 0.5,
577
+ iterations: config?.iterations ?? 10,
578
+ sedimentCapacity: config?.sedimentCapacity ?? 0.1,
579
+ thermalAngle: config?.thermalAngle ?? 45
580
+ };
581
+ for (let x = 0; x < gridSize; x++) {
582
+ for (let z = 0; z < gridSize; z++) {
583
+ this.heightmap.set(`${x},${z}`, 0);
584
+ }
585
+ }
586
+ }
587
+ /**
588
+ * Set heightmap data
589
+ */
590
+ setHeight(x, z, h) {
591
+ this.heightmap.set(`${x},${z}`, h);
592
+ }
593
+ getHeight(x, z) {
594
+ return this.heightmap.get(`${x},${z}`) ?? 0;
595
+ }
596
+ /**
597
+ * Apply erosion at a center position
598
+ */
599
+ erode(cx, cz, overrides) {
600
+ const cfg = { ...this.config, ...overrides };
601
+ let totalErosion = 0;
602
+ let totalDeposition = 0;
603
+ let cellsAffected = 0;
604
+ const r = Math.ceil(cfg.radius);
605
+ for (let iter = 0; iter < cfg.iterations; iter++) {
606
+ for (let dx = -r; dx <= r; dx++) {
607
+ for (let dz = -r; dz <= r; dz++) {
608
+ const dist = Math.sqrt(dx * dx + dz * dz);
609
+ if (dist > cfg.radius) continue;
610
+ const x = cx + dx;
611
+ const z = cz + dz;
612
+ const key = `${x},${z}`;
613
+ const h = this.heightmap.get(key);
614
+ if (h === void 0) continue;
615
+ if (cfg.type === "hydraulic") {
616
+ const neighbors = this.getNeighborHeights(x, z);
617
+ const avgNeighbor = neighbors.reduce((a, b) => a + b, 0) / (neighbors.length || 1);
618
+ const diff = h - avgNeighbor;
619
+ if (diff > 0) {
620
+ const erosion = Math.min(diff * cfg.strength * 0.1, cfg.sedimentCapacity);
621
+ this.heightmap.set(key, h - erosion);
622
+ totalErosion += erosion;
623
+ cellsAffected++;
624
+ } else if (diff < 0) {
625
+ const deposit = Math.abs(diff) * cfg.strength * 0.05;
626
+ this.heightmap.set(key, h + deposit);
627
+ totalDeposition += deposit;
628
+ }
629
+ } else if (cfg.type === "thermal") {
630
+ const neighbors = this.getNeighborHeights(x, z);
631
+ const minNeighbor = Math.min(...neighbors, h);
632
+ const slope = h - minNeighbor;
633
+ const maxSlope = Math.tan(cfg.thermalAngle * Math.PI / 180);
634
+ if (slope > maxSlope) {
635
+ const transfer = (slope - maxSlope) * cfg.strength * 0.1;
636
+ this.heightmap.set(key, h - transfer);
637
+ totalErosion += transfer;
638
+ cellsAffected++;
639
+ }
640
+ } else {
641
+ const neighbors = this.getNeighborHeights(x, z);
642
+ const maxN = Math.max(...neighbors, 0);
643
+ if (h > maxN) {
644
+ const erosion = (h - maxN) * cfg.strength * 0.05;
645
+ this.heightmap.set(key, h - erosion);
646
+ totalErosion += erosion;
647
+ cellsAffected++;
648
+ }
649
+ }
650
+ }
651
+ }
652
+ }
653
+ return { cellsAffected, totalErosion, totalDeposition, iterations: cfg.iterations };
654
+ }
655
+ getNeighborHeights(x, z) {
656
+ const offsets = [
657
+ [-1, 0],
658
+ [1, 0],
659
+ [0, -1],
660
+ [0, 1]
661
+ ];
662
+ const result = [];
663
+ for (const [dx, dz] of offsets) {
664
+ const h = this.heightmap.get(`${x + dx},${z + dz}`);
665
+ if (h !== void 0) result.push(h);
666
+ }
667
+ return result;
668
+ }
669
+ getConfig() {
670
+ return { ...this.config };
671
+ }
672
+ setConfig(cfg) {
673
+ Object.assign(this.config, cfg);
674
+ }
675
+ getGridSize() {
676
+ return this.gridSize;
677
+ }
678
+ };
679
+
680
+ // src/terrain/ErosionSim.ts
681
+ var ErosionSim = class {
682
+ config;
683
+ constructor(config) {
684
+ this.config = {
685
+ iterations: 5e4,
686
+ rainAmount: 1,
687
+ solubility: 0.01,
688
+ evaporationRate: 0.02,
689
+ depositionRate: 0.3,
690
+ gravity: 4,
691
+ thermalAngle: 45,
692
+ thermalRate: 0.5,
693
+ seed: 42,
694
+ ...config
695
+ };
696
+ }
697
+ // ---------------------------------------------------------------------------
698
+ // Hydraulic Erosion (Raindrop)
699
+ // ---------------------------------------------------------------------------
700
+ hydraulicErode(heightmap, width, height) {
701
+ let totalEroded = 0;
702
+ let totalDeposited = 0;
703
+ let maxDepthChange = 0;
704
+ let rng = this.config.seed;
705
+ const rand = () => {
706
+ rng = rng * 1103515245 + 12345 & 2147483647;
707
+ return rng / 2147483647;
708
+ };
709
+ for (let iter = 0; iter < this.config.iterations; iter++) {
710
+ let x = rand() * (width - 2) + 1;
711
+ let z = rand() * (height - 2) + 1;
712
+ let water = this.config.rainAmount;
713
+ let sediment = 0;
714
+ let speed = 1;
715
+ for (let step = 0; step < 64; step++) {
716
+ const ix = Math.floor(x), iz = Math.floor(z);
717
+ if (ix < 1 || ix >= width - 1 || iz < 1 || iz >= height - 1) break;
718
+ const idx = iz * width + ix;
719
+ const h = heightmap[idx];
720
+ const gx = heightmap[idx + 1] - heightmap[idx - 1];
721
+ const gz = heightmap[idx + width] - heightmap[idx - width];
722
+ const gradLen = Math.sqrt(gx * gx + gz * gz) || 1e-3;
723
+ x -= gx / gradLen;
724
+ z -= gz / gradLen;
725
+ const newIx = Math.floor(x), newIz = Math.floor(z);
726
+ if (newIx < 0 || newIx >= width || newIz < 0 || newIz >= height) break;
727
+ const newH = heightmap[newIz * width + newIx];
728
+ const dh = h - newH;
729
+ const capacity = Math.max(dh, 0.01) * speed * water * this.config.solubility;
730
+ if (sediment > capacity || dh < 0) {
731
+ const deposit = dh < 0 ? Math.min(sediment, -dh) : (sediment - capacity) * this.config.depositionRate;
732
+ heightmap[idx] += deposit;
733
+ sediment -= deposit;
734
+ totalDeposited += deposit;
735
+ maxDepthChange = Math.max(maxDepthChange, deposit);
736
+ } else {
737
+ const erosion = Math.min((capacity - sediment) * this.config.solubility, h * 0.1);
738
+ heightmap[idx] -= erosion;
739
+ sediment += erosion;
740
+ totalEroded += erosion;
741
+ maxDepthChange = Math.max(maxDepthChange, erosion);
742
+ }
743
+ speed = Math.sqrt(Math.max(0, speed * speed + dh * this.config.gravity));
744
+ water *= 1 - this.config.evaporationRate;
745
+ if (water < 1e-3) break;
746
+ }
747
+ }
748
+ return { totalEroded, totalDeposited, iterations: this.config.iterations, maxDepthChange };
749
+ }
750
+ // ---------------------------------------------------------------------------
751
+ // Thermal Erosion
752
+ // ---------------------------------------------------------------------------
753
+ thermalErode(heightmap, width, height, iterations) {
754
+ const maxSlope = Math.tan(this.config.thermalAngle * Math.PI / 180);
755
+ const iters = iterations ?? Math.floor(this.config.iterations / 10);
756
+ let totalEroded = 0;
757
+ let totalDeposited = 0;
758
+ let maxDepthChange = 0;
759
+ const offsets = [
760
+ [-1, 0],
761
+ [1, 0],
762
+ [0, -1],
763
+ [0, 1]
764
+ ];
765
+ for (let iter = 0; iter < iters; iter++) {
766
+ for (let z = 1; z < height - 1; z++) {
767
+ for (let x = 1; x < width - 1; x++) {
768
+ const idx = z * width + x;
769
+ const h = heightmap[idx];
770
+ let maxDh = 0;
771
+ let steepestIdx = -1;
772
+ for (const [dx, dz] of offsets) {
773
+ const nIdx = (z + dz) * width + (x + dx);
774
+ const dh = h - heightmap[nIdx];
775
+ if (dh > maxDh) {
776
+ maxDh = dh;
777
+ steepestIdx = nIdx;
778
+ }
779
+ }
780
+ if (maxDh > maxSlope && steepestIdx >= 0) {
781
+ const transfer = (maxDh - maxSlope) * this.config.thermalRate * 0.5;
782
+ heightmap[idx] -= transfer;
783
+ heightmap[steepestIdx] += transfer;
784
+ totalEroded += transfer;
785
+ totalDeposited += transfer;
786
+ maxDepthChange = Math.max(maxDepthChange, transfer);
787
+ }
788
+ }
789
+ }
790
+ }
791
+ return { totalEroded, totalDeposited, iterations: iters, maxDepthChange };
792
+ }
793
+ // ---------------------------------------------------------------------------
794
+ // Config
795
+ // ---------------------------------------------------------------------------
796
+ setConfig(config) {
797
+ Object.assign(this.config, config);
798
+ }
799
+ getConfig() {
800
+ return { ...this.config };
801
+ }
802
+ };
803
+
804
+ export {
805
+ TerrainBrush,
806
+ TerrainLOD,
807
+ TerrainPaintLayer,
808
+ TerrainTexturing,
809
+ ErosionBrush,
810
+ ErosionSim,
811
+ terrain_exports
812
+ };