@adatechnology/game-vehicles 0.1.12 → 0.3.0

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.
@@ -30,9 +30,12 @@ var GameVehicles = (() => {
30
30
  buildVehicleItem: () => buildVehicleItem,
31
31
  buildVehicleModel: () => buildVehicleModel,
32
32
  clamp: () => clamp,
33
+ createLod: () => createLod,
33
34
  createTranslate: () => createTranslate,
34
35
  createVehicleDestructible: () => createVehicleDestructible,
35
36
  createVehicleKit: () => createVehicleKit,
37
+ defaultDamageableKinds: () => defaultDamageableKinds,
38
+ defaultLodSettings: () => defaultLodSettings,
36
39
  format: () => format,
37
40
  hash: () => hash,
38
41
  legacyGlassId: () => legacyGlassId,
@@ -44,9 +47,1527 @@ var GameVehicles = (() => {
44
47
  resolveStrings: () => resolveStrings,
45
48
  rng: () => rng,
46
49
  smooth: () => smooth,
47
- strHash: () => strHash
50
+ strHash: () => strHash,
51
+ wrapThreeForLod: () => wrapThreeForLod
48
52
  });
49
53
 
54
+ // ../game-shapes/dist/chunk-R4RY23RH.js
55
+ function canonicalJson(value) {
56
+ if (value === null) return "null";
57
+ if (typeof value === "number") return Object.is(value, -0) ? "-0" : Number.isFinite(value) ? String(value) : JSON.stringify(String(value));
58
+ if (typeof value === "string") return JSON.stringify(value);
59
+ if (typeof value === "boolean") return value ? "true" : "false";
60
+ if (typeof value === "function") throw new Error("game-shapes: a function cannot be part of a shape (register it as a generator)");
61
+ if (ArrayBuffer.isView(value)) return canonicalJson(Array.from(value));
62
+ if (Array.isArray(value)) return `[${value.map((item) => item === void 0 ? "null" : canonicalJson(item)).join(",")}]`;
63
+ if (typeof value === "object") {
64
+ const record = value;
65
+ const keys = Object.keys(record).filter((key) => record[key] !== void 0).sort();
66
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
67
+ }
68
+ return "null";
69
+ }
70
+ function needsUint32(array) {
71
+ for (let i = array.length - 1; i >= 0; --i) if (array[i] >= 65535) return true;
72
+ return false;
73
+ }
74
+ var indexArray = (indices) => needsUint32(indices) ? new Uint32Array(indices) : new Uint16Array(indices);
75
+ function pack(type, parameters, a) {
76
+ return {
77
+ type,
78
+ parameters,
79
+ positions: new Float32Array(a.vertices),
80
+ normals: new Float32Array(a.normals),
81
+ uvs: new Float32Array(a.uvs),
82
+ ...a.indices ? { indices: indexArray(a.indices) } : {},
83
+ groups: a.groups
84
+ };
85
+ }
86
+ var normalize3 = (x, y, z) => {
87
+ const length = Math.sqrt(x * x + y * y + z * z);
88
+ const s = 1 / (length || 1);
89
+ return [x * s, y * s, z * s];
90
+ };
91
+ function box(width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1) {
92
+ const parameters = { width, height, depth, widthSegments, heightSegments, depthSegments };
93
+ widthSegments = Math.floor(widthSegments);
94
+ heightSegments = Math.floor(heightSegments);
95
+ depthSegments = Math.floor(depthSegments);
96
+ const indices = [];
97
+ const vertices = [];
98
+ const normals = [];
99
+ const uvs = [];
100
+ const groups = [];
101
+ let numberOfVertices = 0;
102
+ let groupStart = 0;
103
+ const buildPlane = (u, v, w, udir, vdir, width2, height2, depth2, gridX, gridY, materialIndex) => {
104
+ const segmentWidth = width2 / gridX;
105
+ const segmentHeight = height2 / gridY;
106
+ const widthHalf = width2 / 2;
107
+ const heightHalf = height2 / 2;
108
+ const depthHalf = depth2 / 2;
109
+ const gridX1 = gridX + 1;
110
+ const gridY1 = gridY + 1;
111
+ let vertexCounter = 0;
112
+ let groupCount = 0;
113
+ const vector = { x: 0, y: 0, z: 0 };
114
+ for (let iy = 0; iy < gridY1; iy++) {
115
+ const y = iy * segmentHeight - heightHalf;
116
+ for (let ix = 0; ix < gridX1; ix++) {
117
+ const x = ix * segmentWidth - widthHalf;
118
+ vector[u] = x * udir;
119
+ vector[v] = y * vdir;
120
+ vector[w] = depthHalf;
121
+ vertices.push(vector.x, vector.y, vector.z);
122
+ vector[u] = 0;
123
+ vector[v] = 0;
124
+ vector[w] = depth2 > 0 ? 1 : -1;
125
+ normals.push(vector.x, vector.y, vector.z);
126
+ uvs.push(ix / gridX);
127
+ uvs.push(1 - iy / gridY);
128
+ vertexCounter += 1;
129
+ }
130
+ }
131
+ for (let iy = 0; iy < gridY; iy++) {
132
+ for (let ix = 0; ix < gridX; ix++) {
133
+ const a = numberOfVertices + ix + gridX1 * iy;
134
+ const b = numberOfVertices + ix + gridX1 * (iy + 1);
135
+ const c = numberOfVertices + (ix + 1) + gridX1 * (iy + 1);
136
+ const d = numberOfVertices + (ix + 1) + gridX1 * iy;
137
+ indices.push(a, b, d);
138
+ indices.push(b, c, d);
139
+ groupCount += 6;
140
+ }
141
+ }
142
+ groups.push({ start: groupStart, count: groupCount, materialIndex });
143
+ groupStart += groupCount;
144
+ numberOfVertices += vertexCounter;
145
+ };
146
+ buildPlane("z", "y", "x", -1, -1, depth, height, width, depthSegments, heightSegments, 0);
147
+ buildPlane("z", "y", "x", 1, -1, depth, height, -width, depthSegments, heightSegments, 1);
148
+ buildPlane("x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2);
149
+ buildPlane("x", "z", "y", 1, -1, width, depth, -height, widthSegments, depthSegments, 3);
150
+ buildPlane("x", "y", "z", 1, -1, width, height, depth, widthSegments, heightSegments, 4);
151
+ buildPlane("x", "y", "z", -1, -1, width, height, -depth, widthSegments, heightSegments, 5);
152
+ return pack("BoxGeometry", parameters, { vertices, normals, uvs, indices, groups });
153
+ }
154
+ function cylinderArrays(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) {
155
+ radialSegments = Math.floor(radialSegments);
156
+ heightSegments = Math.floor(heightSegments);
157
+ const indices = [];
158
+ const vertices = [];
159
+ const normals = [];
160
+ const uvs = [];
161
+ const groups = [];
162
+ let index = 0;
163
+ const indexArray2 = [];
164
+ const halfHeight = height / 2;
165
+ let groupStart = 0;
166
+ const generateTorso = () => {
167
+ let groupCount = 0;
168
+ const slope = (radiusBottom - radiusTop) / height;
169
+ for (let y = 0; y <= heightSegments; y++) {
170
+ const indexRow = [];
171
+ const v = y / heightSegments;
172
+ const radius = v * (radiusBottom - radiusTop) + radiusTop;
173
+ for (let x = 0; x <= radialSegments; x++) {
174
+ const u = x / radialSegments;
175
+ const theta = u * thetaLength + thetaStart;
176
+ const sinTheta = Math.sin(theta);
177
+ const cosTheta = Math.cos(theta);
178
+ vertices.push(radius * sinTheta, -v * height + halfHeight, radius * cosTheta);
179
+ const n = normalize3(sinTheta, slope, cosTheta);
180
+ normals.push(n[0], n[1], n[2]);
181
+ uvs.push(u, 1 - v);
182
+ indexRow.push(index++);
183
+ }
184
+ indexArray2.push(indexRow);
185
+ }
186
+ for (let x = 0; x < radialSegments; x++) {
187
+ for (let y = 0; y < heightSegments; y++) {
188
+ const a = indexArray2[y][x];
189
+ const b = indexArray2[y + 1][x];
190
+ const c = indexArray2[y + 1][x + 1];
191
+ const d = indexArray2[y][x + 1];
192
+ indices.push(a, b, d);
193
+ indices.push(b, c, d);
194
+ groupCount += 6;
195
+ }
196
+ }
197
+ groups.push({ start: groupStart, count: groupCount, materialIndex: 0 });
198
+ groupStart += groupCount;
199
+ };
200
+ const generateCap = (top) => {
201
+ const centerIndexStart = index;
202
+ let groupCount = 0;
203
+ const radius = top === true ? radiusTop : radiusBottom;
204
+ const sign2 = top === true ? 1 : -1;
205
+ for (let x = 1; x <= radialSegments; x++) {
206
+ vertices.push(0, halfHeight * sign2, 0);
207
+ normals.push(0, sign2, 0);
208
+ uvs.push(0.5, 0.5);
209
+ index++;
210
+ }
211
+ const centerIndexEnd = index;
212
+ for (let x = 0; x <= radialSegments; x++) {
213
+ const u = x / radialSegments;
214
+ const theta = u * thetaLength + thetaStart;
215
+ const cosTheta = Math.cos(theta);
216
+ const sinTheta = Math.sin(theta);
217
+ vertices.push(radius * sinTheta, halfHeight * sign2, radius * cosTheta);
218
+ normals.push(0, sign2, 0);
219
+ uvs.push(cosTheta * 0.5 + 0.5, sinTheta * 0.5 * sign2 + 0.5);
220
+ index++;
221
+ }
222
+ for (let x = 0; x < radialSegments; x++) {
223
+ const c = centerIndexStart + x;
224
+ const i = centerIndexEnd + x;
225
+ if (top === true) indices.push(i, i + 1, c);
226
+ else indices.push(i + 1, i, c);
227
+ groupCount += 3;
228
+ }
229
+ groups.push({ start: groupStart, count: groupCount, materialIndex: top === true ? 1 : 2 });
230
+ groupStart += groupCount;
231
+ };
232
+ generateTorso();
233
+ if (openEnded === false) {
234
+ if (radiusTop > 0) generateCap(true);
235
+ if (radiusBottom > 0) generateCap(false);
236
+ }
237
+ return { vertices, normals, uvs, indices, groups };
238
+ }
239
+ function cylinder(radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) {
240
+ const parameters = { radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength };
241
+ return pack("CylinderGeometry", parameters, cylinderArrays(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength));
242
+ }
243
+ function cone(radius = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) {
244
+ const parameters = { radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength };
245
+ return pack("ConeGeometry", parameters, cylinderArrays(0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength));
246
+ }
247
+ function sphere(radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI) {
248
+ const parameters = { radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength };
249
+ widthSegments = Math.max(3, Math.floor(widthSegments));
250
+ heightSegments = Math.max(2, Math.floor(heightSegments));
251
+ const thetaEnd = Math.min(thetaStart + thetaLength, Math.PI);
252
+ let index = 0;
253
+ const grid = [];
254
+ const indices = [];
255
+ const vertices = [];
256
+ const normals = [];
257
+ const uvs = [];
258
+ for (let iy = 0; iy <= heightSegments; iy++) {
259
+ const verticesRow = [];
260
+ const v = iy / heightSegments;
261
+ let uOffset = 0;
262
+ if (iy === 0 && thetaStart === 0) uOffset = 0.5 / widthSegments;
263
+ else if (iy === heightSegments && thetaEnd === Math.PI) uOffset = -0.5 / widthSegments;
264
+ for (let ix = 0; ix <= widthSegments; ix++) {
265
+ const u = ix / widthSegments;
266
+ const x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength);
267
+ const y = radius * Math.cos(thetaStart + v * thetaLength);
268
+ const z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength);
269
+ vertices.push(x, y, z);
270
+ const n = normalize3(x, y, z);
271
+ normals.push(n[0], n[1], n[2]);
272
+ uvs.push(u + uOffset, 1 - v);
273
+ verticesRow.push(index++);
274
+ }
275
+ grid.push(verticesRow);
276
+ }
277
+ for (let iy = 0; iy < heightSegments; iy++) {
278
+ for (let ix = 0; ix < widthSegments; ix++) {
279
+ const a = grid[iy][ix + 1];
280
+ const b = grid[iy][ix];
281
+ const c = grid[iy + 1][ix];
282
+ const d = grid[iy + 1][ix + 1];
283
+ if (iy !== 0 || thetaStart > 0) indices.push(a, b, d);
284
+ if (iy !== heightSegments - 1 || thetaEnd < Math.PI) indices.push(b, c, d);
285
+ }
286
+ }
287
+ return pack("SphereGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
288
+ }
289
+ function plane(width = 1, height = 1, widthSegments = 1, heightSegments = 1) {
290
+ const parameters = { width, height, widthSegments, heightSegments };
291
+ const widthHalf = width / 2;
292
+ const heightHalf = height / 2;
293
+ const gridX = Math.floor(widthSegments);
294
+ const gridY = Math.floor(heightSegments);
295
+ const gridX1 = gridX + 1;
296
+ const gridY1 = gridY + 1;
297
+ const segmentWidth = width / gridX;
298
+ const segmentHeight = height / gridY;
299
+ const indices = [];
300
+ const vertices = [];
301
+ const normals = [];
302
+ const uvs = [];
303
+ for (let iy = 0; iy < gridY1; iy++) {
304
+ const y = iy * segmentHeight - heightHalf;
305
+ for (let ix = 0; ix < gridX1; ix++) {
306
+ const x = ix * segmentWidth - widthHalf;
307
+ vertices.push(x, -y, 0);
308
+ normals.push(0, 0, 1);
309
+ uvs.push(ix / gridX);
310
+ uvs.push(1 - iy / gridY);
311
+ }
312
+ }
313
+ for (let iy = 0; iy < gridY; iy++) {
314
+ for (let ix = 0; ix < gridX; ix++) {
315
+ const a = ix + gridX1 * iy;
316
+ const b = ix + gridX1 * (iy + 1);
317
+ const c = ix + 1 + gridX1 * (iy + 1);
318
+ const d = ix + 1 + gridX1 * iy;
319
+ indices.push(a, b, d);
320
+ indices.push(b, c, d);
321
+ }
322
+ }
323
+ return pack("PlaneGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
324
+ }
325
+ function circle(radius = 1, segments = 32, thetaStart = 0, thetaLength = Math.PI * 2) {
326
+ const parameters = { radius, segments, thetaStart, thetaLength };
327
+ segments = Math.max(3, segments);
328
+ const indices = [];
329
+ const vertices = [];
330
+ const normals = [];
331
+ const uvs = [];
332
+ vertices.push(0, 0, 0);
333
+ normals.push(0, 0, 1);
334
+ uvs.push(0.5, 0.5);
335
+ for (let s = 0, i = 3; s <= segments; s++, i += 3) {
336
+ const segment = thetaStart + s / segments * thetaLength;
337
+ vertices.push(radius * Math.cos(segment), radius * Math.sin(segment), 0);
338
+ normals.push(0, 0, 1);
339
+ uvs.push((vertices[i] / radius + 1) / 2, (vertices[i + 1] / radius + 1) / 2);
340
+ }
341
+ for (let i = 1; i <= segments; i++) indices.push(i, i + 1, 0);
342
+ return pack("CircleGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
343
+ }
344
+ function ring(innerRadius = 0.5, outerRadius = 1, thetaSegments = 32, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2) {
345
+ const parameters = { innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength };
346
+ thetaSegments = Math.max(3, thetaSegments);
347
+ phiSegments = Math.max(1, phiSegments);
348
+ const indices = [];
349
+ const vertices = [];
350
+ const normals = [];
351
+ const uvs = [];
352
+ let radius = innerRadius;
353
+ const radiusStep = (outerRadius - innerRadius) / phiSegments;
354
+ for (let j = 0; j <= phiSegments; j++) {
355
+ for (let i = 0; i <= thetaSegments; i++) {
356
+ const segment = thetaStart + i / thetaSegments * thetaLength;
357
+ const x = radius * Math.cos(segment);
358
+ const y = radius * Math.sin(segment);
359
+ vertices.push(x, y, 0);
360
+ normals.push(0, 0, 1);
361
+ uvs.push((x / outerRadius + 1) / 2, (y / outerRadius + 1) / 2);
362
+ }
363
+ radius += radiusStep;
364
+ }
365
+ for (let j = 0; j < phiSegments; j++) {
366
+ const thetaSegmentLevel = j * (thetaSegments + 1);
367
+ for (let i = 0; i < thetaSegments; i++) {
368
+ const segment = i + thetaSegmentLevel;
369
+ const a = segment;
370
+ const b = segment + thetaSegments + 1;
371
+ const c = segment + thetaSegments + 2;
372
+ const d = segment + 1;
373
+ indices.push(a, b, d);
374
+ indices.push(b, c, d);
375
+ }
376
+ }
377
+ return pack("RingGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
378
+ }
379
+ function lathe(points, segments = 12, phiStart = 0, phiLength = Math.PI * 2) {
380
+ const parameters = { points: points.map(([x, y]) => ({ x, y })), segments, phiStart, phiLength };
381
+ segments = Math.floor(segments);
382
+ phiLength = Math.max(0, Math.min(Math.PI * 2, phiLength));
383
+ const indices = [];
384
+ const vertices = [];
385
+ const uvs = [];
386
+ const initNormals = [];
387
+ const normals = [];
388
+ const inverseSegments = 1 / segments;
389
+ const normal = { x: 0, y: 0, z: 0 };
390
+ const curNormal = { x: 0, y: 0, z: 0 };
391
+ const prevNormal = { x: 0, y: 0, z: 0 };
392
+ let dx = 0;
393
+ let dy = 0;
394
+ const last = points.length - 1;
395
+ const px = (j) => points[j][0];
396
+ const py = (j) => points[j][1];
397
+ const setNormalized = () => {
398
+ const n = normalize3(normal.x, normal.y, normal.z);
399
+ normal.x = n[0];
400
+ normal.y = n[1];
401
+ normal.z = n[2];
402
+ };
403
+ for (let j = 0; j <= last; j++) {
404
+ if (j === 0) {
405
+ dx = px(j + 1) - px(j);
406
+ dy = py(j + 1) - py(j);
407
+ normal.x = dy * 1;
408
+ normal.y = -dx;
409
+ normal.z = dy * 0;
410
+ prevNormal.x = normal.x;
411
+ prevNormal.y = normal.y;
412
+ prevNormal.z = normal.z;
413
+ setNormalized();
414
+ initNormals.push(normal.x, normal.y, normal.z);
415
+ } else if (j === last) {
416
+ initNormals.push(prevNormal.x, prevNormal.y, prevNormal.z);
417
+ } else {
418
+ dx = px(j + 1) - px(j);
419
+ dy = py(j + 1) - py(j);
420
+ normal.x = dy * 1;
421
+ normal.y = -dx;
422
+ normal.z = dy * 0;
423
+ curNormal.x = normal.x;
424
+ curNormal.y = normal.y;
425
+ curNormal.z = normal.z;
426
+ normal.x += prevNormal.x;
427
+ normal.y += prevNormal.y;
428
+ normal.z += prevNormal.z;
429
+ setNormalized();
430
+ initNormals.push(normal.x, normal.y, normal.z);
431
+ prevNormal.x = curNormal.x;
432
+ prevNormal.y = curNormal.y;
433
+ prevNormal.z = curNormal.z;
434
+ }
435
+ }
436
+ for (let i = 0; i <= segments; i++) {
437
+ const phi = phiStart + i * inverseSegments * phiLength;
438
+ const sin = Math.sin(phi);
439
+ const cos = Math.cos(phi);
440
+ for (let j = 0; j <= last; j++) {
441
+ vertices.push(px(j) * sin, py(j), px(j) * cos);
442
+ uvs.push(i / segments, j / (points.length - 1));
443
+ const x = initNormals[3 * j + 0] * sin;
444
+ const y = initNormals[3 * j + 1];
445
+ const z = initNormals[3 * j + 0] * cos;
446
+ normals.push(x, y, z);
447
+ }
448
+ }
449
+ for (let i = 0; i < segments; i++) {
450
+ for (let j = 0; j < points.length - 1; j++) {
451
+ const base = j + i * points.length;
452
+ const a = base;
453
+ const b = base + points.length;
454
+ const c = base + points.length + 1;
455
+ const d = base + 1;
456
+ indices.push(a, b, d);
457
+ indices.push(c, d, b);
458
+ }
459
+ }
460
+ return pack("LatheGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
461
+ }
462
+ function torus(radius = 1, tube = 0.4, radialSegments = 12, tubularSegments = 48, arc = Math.PI * 2) {
463
+ const parameters = { radius, tube, radialSegments, tubularSegments, arc };
464
+ radialSegments = Math.floor(radialSegments);
465
+ tubularSegments = Math.floor(tubularSegments);
466
+ const indices = [];
467
+ const vertices = [];
468
+ const normals = [];
469
+ const uvs = [];
470
+ for (let j = 0; j <= radialSegments; j++) {
471
+ for (let i = 0; i <= tubularSegments; i++) {
472
+ const u = i / tubularSegments * arc;
473
+ const v = j / radialSegments * Math.PI * 2;
474
+ const x = (radius + tube * Math.cos(v)) * Math.cos(u);
475
+ const y = (radius + tube * Math.cos(v)) * Math.sin(u);
476
+ const z = tube * Math.sin(v);
477
+ vertices.push(x, y, z);
478
+ const cx = radius * Math.cos(u);
479
+ const cy = radius * Math.sin(u);
480
+ const n = normalize3(x - cx, y - cy, z - 0);
481
+ normals.push(n[0], n[1], n[2]);
482
+ uvs.push(i / tubularSegments);
483
+ uvs.push(j / radialSegments);
484
+ }
485
+ }
486
+ for (let j = 1; j <= radialSegments; j++) {
487
+ for (let i = 1; i <= tubularSegments; i++) {
488
+ const a = (tubularSegments + 1) * j + i - 1;
489
+ const b = (tubularSegments + 1) * (j - 1) + i - 1;
490
+ const c = (tubularSegments + 1) * (j - 1) + i;
491
+ const d = (tubularSegments + 1) * j + i;
492
+ indices.push(a, b, d);
493
+ indices.push(b, c, d);
494
+ }
495
+ }
496
+ return pack("TorusGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
497
+ }
498
+ function difference(size, subtract) {
499
+ const [w, h, d] = size;
500
+ const hw = w / 2;
501
+ const hh = h / 2;
502
+ const hd = d / 2;
503
+ const holes = subtract.map((hole) => ({ x0: Math.max(-hw, Math.min(hole.min[0], hole.max[0])), x1: Math.min(hw, Math.max(hole.min[0], hole.max[0])), y0: Math.max(-hh, Math.min(hole.min[1], hole.max[1])), y1: Math.min(hh, Math.max(hole.min[1], hole.max[1])) })).filter((hole) => hole.x1 > hole.x0 && hole.y1 > hole.y0);
504
+ const cuts = (lo, hi, values) => [.../* @__PURE__ */ new Set([lo, hi, ...values.filter((v) => v > lo && v < hi)])].sort((a, b) => a - b);
505
+ const xs = cuts(-hw, hw, holes.flatMap((hole) => [hole.x0, hole.x1]));
506
+ const ys = cuts(-hh, hh, holes.flatMap((hole) => [hole.y0, hole.y1]));
507
+ const nx = xs.length - 1;
508
+ const ny = ys.length - 1;
509
+ const solid = (i, j) => {
510
+ if (i < 0 || j < 0 || i >= nx || j >= ny) return false;
511
+ const cx = (xs[i] + xs[i + 1]) / 2;
512
+ const cy = (ys[j] + ys[j + 1]) / 2;
513
+ return !holes.some((hole) => cx > hole.x0 && cx < hole.x1 && cy > hole.y0 && cy < hole.y1);
514
+ };
515
+ const positions = [];
516
+ const normals = [];
517
+ const uvs = [];
518
+ const indices = [];
519
+ const quad = (corners, normal, uv) => {
520
+ const base = positions.length / 3;
521
+ for (let k = 0; k < 4; k++) {
522
+ positions.push(...corners[k]);
523
+ normals.push(...normal);
524
+ uvs.push(...uv[k]);
525
+ }
526
+ indices.push(base, base + 1, base + 2, base, base + 2, base + 3);
527
+ };
528
+ const fu = (x) => (x + hw) / w;
529
+ const fv = (y) => (y + hh) / h;
530
+ for (let j = 0; j < ny; j++) {
531
+ for (let i = 0; i < nx; i++) {
532
+ if (!solid(i, j)) continue;
533
+ const x0 = xs[i];
534
+ const x1 = xs[i + 1];
535
+ const y0 = ys[j];
536
+ const y1 = ys[j + 1];
537
+ quad([[x0, y0, hd], [x1, y0, hd], [x1, y1, hd], [x0, y1, hd]], [0, 0, 1], [[fu(x0), fv(y0)], [fu(x1), fv(y0)], [fu(x1), fv(y1)], [fu(x0), fv(y1)]]);
538
+ quad([[x1, y0, -hd], [x0, y0, -hd], [x0, y1, -hd], [x1, y1, -hd]], [0, 0, -1], [[1 - fu(x1), fv(y0)], [1 - fu(x0), fv(y0)], [1 - fu(x0), fv(y1)], [1 - fu(x1), fv(y1)]]);
539
+ const zu = (z) => (z + hd) / d;
540
+ if (!solid(i - 1, j)) quad([[x0, y0, -hd], [x0, y0, hd], [x0, y1, hd], [x0, y1, -hd]], [-1, 0, 0], [[zu(-hd), fv(y0)], [zu(hd), fv(y0)], [zu(hd), fv(y1)], [zu(-hd), fv(y1)]]);
541
+ if (!solid(i + 1, j)) quad([[x1, y0, hd], [x1, y0, -hd], [x1, y1, -hd], [x1, y1, hd]], [1, 0, 0], [[zu(hd), fv(y0)], [zu(-hd), fv(y0)], [zu(-hd), fv(y1)], [zu(hd), fv(y1)]]);
542
+ if (!solid(i, j - 1)) quad([[x0, y0, -hd], [x1, y0, -hd], [x1, y0, hd], [x0, y0, hd]], [0, -1, 0], [[fu(x0), zu(-hd)], [fu(x1), zu(-hd)], [fu(x1), zu(hd)], [fu(x0), zu(hd)]]);
543
+ if (!solid(i, j + 1)) quad([[x0, y1, hd], [x1, y1, hd], [x1, y1, -hd], [x0, y1, -hd]], [0, 1, 0], [[fu(x0), zu(hd)], [fu(x1), zu(hd)], [fu(x1), zu(-hd)], [fu(x0), zu(-hd)]]);
544
+ }
545
+ }
546
+ return {
547
+ type: "BufferGeometry",
548
+ parameters: { size: [w, h, d], subtract: holes.map((hole) => ({ min: [hole.x0, hole.y0], max: [hole.x1, hole.y1] })) },
549
+ positions: new Float32Array(positions),
550
+ normals: new Float32Array(normals),
551
+ uvs: new Float32Array(uvs),
552
+ indices: indexArray(indices),
553
+ groups: []
554
+ };
555
+ }
556
+ var Earcut = {
557
+ triangulate: function(data, holeIndices, dim = 2) {
558
+ const hasHoles = holeIndices && holeIndices.length;
559
+ const outerLen = hasHoles ? holeIndices[0] * dim : data.length;
560
+ let outerNode = linkedList(data, 0, outerLen, dim, true);
561
+ const triangles = [];
562
+ if (!outerNode || outerNode.next === outerNode.prev) return triangles;
563
+ let minX, minY, maxX, maxY, x, y, invSize;
564
+ if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
565
+ if (data.length > 80 * dim) {
566
+ minX = maxX = data[0];
567
+ minY = maxY = data[1];
568
+ for (let i = dim; i < outerLen; i += dim) {
569
+ x = data[i];
570
+ y = data[i + 1];
571
+ if (x < minX) minX = x;
572
+ if (y < minY) minY = y;
573
+ if (x > maxX) maxX = x;
574
+ if (y > maxY) maxY = y;
575
+ }
576
+ invSize = Math.max(maxX - minX, maxY - minY);
577
+ invSize = invSize !== 0 ? 32767 / invSize : 0;
578
+ }
579
+ earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0);
580
+ return triangles;
581
+ }
582
+ };
583
+ function linkedList(data, start, end, dim, clockwise) {
584
+ let i, last;
585
+ if (clockwise === signedArea(data, start, end, dim) > 0) {
586
+ for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last);
587
+ } else {
588
+ for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last);
589
+ }
590
+ if (last && equals(last, last.next)) {
591
+ removeNode(last);
592
+ last = last.next;
593
+ }
594
+ return last;
595
+ }
596
+ function filterPoints(start, end) {
597
+ if (!start) return start;
598
+ if (!end) end = start;
599
+ let p = start, again;
600
+ do {
601
+ again = false;
602
+ if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
603
+ removeNode(p);
604
+ p = end = p.prev;
605
+ if (p === p.next) break;
606
+ again = true;
607
+ } else {
608
+ p = p.next;
609
+ }
610
+ } while (again || p !== end);
611
+ return end;
612
+ }
613
+ function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
614
+ if (!ear) return;
615
+ if (!pass && invSize) indexCurve(ear, minX, minY, invSize);
616
+ let stop = ear, prev, next;
617
+ while (ear.prev !== ear.next) {
618
+ prev = ear.prev;
619
+ next = ear.next;
620
+ if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
621
+ triangles.push(prev.i / dim | 0);
622
+ triangles.push(ear.i / dim | 0);
623
+ triangles.push(next.i / dim | 0);
624
+ removeNode(ear);
625
+ ear = next.next;
626
+ stop = next.next;
627
+ continue;
628
+ }
629
+ ear = next;
630
+ if (ear === stop) {
631
+ if (!pass) {
632
+ earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);
633
+ } else if (pass === 1) {
634
+ ear = cureLocalIntersections(filterPoints(ear), triangles, dim);
635
+ earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);
636
+ } else if (pass === 2) {
637
+ splitEarcut(ear, triangles, dim, minX, minY, invSize);
638
+ }
639
+ break;
640
+ }
641
+ }
642
+ }
643
+ function isEar(ear) {
644
+ const a = ear.prev, b = ear, c = ear.next;
645
+ if (area(a, b, c) >= 0) return false;
646
+ const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
647
+ const x0 = ax < bx ? ax < cx ? ax : cx : bx < cx ? bx : cx, y0 = ay < by ? ay < cy ? ay : cy : by < cy ? by : cy, x1 = ax > bx ? ax > cx ? ax : cx : bx > cx ? bx : cx, y1 = ay > by ? ay > cy ? ay : cy : by > cy ? by : cy;
648
+ let p = c.next;
649
+ while (p !== a) {
650
+ if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
651
+ p = p.next;
652
+ }
653
+ return true;
654
+ }
655
+ function isEarHashed(ear, minX, minY, invSize) {
656
+ const a = ear.prev, b = ear, c = ear.next;
657
+ if (area(a, b, c) >= 0) return false;
658
+ const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
659
+ const x0 = ax < bx ? ax < cx ? ax : cx : bx < cx ? bx : cx, y0 = ay < by ? ay < cy ? ay : cy : by < cy ? by : cy, x1 = ax > bx ? ax > cx ? ax : cx : bx > cx ? bx : cx, y1 = ay > by ? ay > cy ? ay : cy : by > cy ? by : cy;
660
+ const minZ = zOrder(x0, y0, minX, minY, invSize), maxZ = zOrder(x1, y1, minX, minY, invSize);
661
+ let p = ear.prevZ, n = ear.nextZ;
662
+ while (p && p.z >= minZ && n && n.z <= maxZ) {
663
+ if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
664
+ p = p.prevZ;
665
+ if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
666
+ n = n.nextZ;
667
+ }
668
+ while (p && p.z >= minZ) {
669
+ if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
670
+ p = p.prevZ;
671
+ }
672
+ while (n && n.z <= maxZ) {
673
+ if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
674
+ n = n.nextZ;
675
+ }
676
+ return true;
677
+ }
678
+ function cureLocalIntersections(start, triangles, dim) {
679
+ let p = start;
680
+ do {
681
+ const a = p.prev, b = p.next.next;
682
+ if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
683
+ triangles.push(a.i / dim | 0);
684
+ triangles.push(p.i / dim | 0);
685
+ triangles.push(b.i / dim | 0);
686
+ removeNode(p);
687
+ removeNode(p.next);
688
+ p = start = b;
689
+ }
690
+ p = p.next;
691
+ } while (p !== start);
692
+ return filterPoints(p);
693
+ }
694
+ function splitEarcut(start, triangles, dim, minX, minY, invSize) {
695
+ let a = start;
696
+ do {
697
+ let b = a.next.next;
698
+ while (b !== a.prev) {
699
+ if (a.i !== b.i && isValidDiagonal(a, b)) {
700
+ let c = splitPolygon(a, b);
701
+ a = filterPoints(a, a.next);
702
+ c = filterPoints(c, c.next);
703
+ earcutLinked(a, triangles, dim, minX, minY, invSize, 0);
704
+ earcutLinked(c, triangles, dim, minX, minY, invSize, 0);
705
+ return;
706
+ }
707
+ b = b.next;
708
+ }
709
+ a = a.next;
710
+ } while (a !== start);
711
+ }
712
+ function eliminateHoles(data, holeIndices, outerNode, dim) {
713
+ const queue = [];
714
+ let i, len, start, end, list2;
715
+ for (i = 0, len = holeIndices.length; i < len; i++) {
716
+ start = holeIndices[i] * dim;
717
+ end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
718
+ list2 = linkedList(data, start, end, dim, false);
719
+ if (list2 === list2.next) list2.steiner = true;
720
+ queue.push(getLeftmost(list2));
721
+ }
722
+ queue.sort(compareX);
723
+ for (i = 0; i < queue.length; i++) {
724
+ outerNode = eliminateHole(queue[i], outerNode);
725
+ }
726
+ return outerNode;
727
+ }
728
+ function compareX(a, b) {
729
+ return a.x - b.x;
730
+ }
731
+ function eliminateHole(hole, outerNode) {
732
+ const bridge = findHoleBridge(hole, outerNode);
733
+ if (!bridge) {
734
+ return outerNode;
735
+ }
736
+ const bridgeReverse = splitPolygon(bridge, hole);
737
+ filterPoints(bridgeReverse, bridgeReverse.next);
738
+ return filterPoints(bridge, bridge.next);
739
+ }
740
+ function findHoleBridge(hole, outerNode) {
741
+ let p = outerNode, qx = -Infinity, m;
742
+ const hx = hole.x, hy = hole.y;
743
+ do {
744
+ if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
745
+ const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
746
+ if (x <= hx && x > qx) {
747
+ qx = x;
748
+ m = p.x < p.next.x ? p : p.next;
749
+ if (x === hx) return m;
750
+ }
751
+ }
752
+ p = p.next;
753
+ } while (p !== outerNode);
754
+ if (!m) return null;
755
+ const stop = m, mx = m.x, my = m.y;
756
+ let tanMin = Infinity, tan;
757
+ p = m;
758
+ do {
759
+ if (hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {
760
+ tan = Math.abs(hy - p.y) / (hx - p.x);
761
+ if (locallyInside(p, hole) && (tan < tanMin || tan === tanMin && (p.x > m.x || p.x === m.x && sectorContainsSector(m, p)))) {
762
+ m = p;
763
+ tanMin = tan;
764
+ }
765
+ }
766
+ p = p.next;
767
+ } while (p !== stop);
768
+ return m;
769
+ }
770
+ function sectorContainsSector(m, p) {
771
+ return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0;
772
+ }
773
+ function indexCurve(start, minX, minY, invSize) {
774
+ let p = start;
775
+ do {
776
+ if (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize);
777
+ p.prevZ = p.prev;
778
+ p.nextZ = p.next;
779
+ p = p.next;
780
+ } while (p !== start);
781
+ p.prevZ.nextZ = null;
782
+ p.prevZ = null;
783
+ sortLinked(p);
784
+ }
785
+ function sortLinked(list2) {
786
+ let i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1;
787
+ do {
788
+ p = list2;
789
+ list2 = null;
790
+ tail = null;
791
+ numMerges = 0;
792
+ while (p) {
793
+ numMerges++;
794
+ q = p;
795
+ pSize = 0;
796
+ for (i = 0; i < inSize; i++) {
797
+ pSize++;
798
+ q = q.nextZ;
799
+ if (!q) break;
800
+ }
801
+ qSize = inSize;
802
+ while (pSize > 0 || qSize > 0 && q) {
803
+ if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) {
804
+ e = p;
805
+ p = p.nextZ;
806
+ pSize--;
807
+ } else {
808
+ e = q;
809
+ q = q.nextZ;
810
+ qSize--;
811
+ }
812
+ if (tail) tail.nextZ = e;
813
+ else list2 = e;
814
+ e.prevZ = tail;
815
+ tail = e;
816
+ }
817
+ p = q;
818
+ }
819
+ tail.nextZ = null;
820
+ inSize *= 2;
821
+ } while (numMerges > 1);
822
+ return list2;
823
+ }
824
+ function zOrder(x, y, minX, minY, invSize) {
825
+ x = (x - minX) * invSize | 0;
826
+ y = (y - minY) * invSize | 0;
827
+ x = (x | x << 8) & 16711935;
828
+ x = (x | x << 4) & 252645135;
829
+ x = (x | x << 2) & 858993459;
830
+ x = (x | x << 1) & 1431655765;
831
+ y = (y | y << 8) & 16711935;
832
+ y = (y | y << 4) & 252645135;
833
+ y = (y | y << 2) & 858993459;
834
+ y = (y | y << 1) & 1431655765;
835
+ return x | y << 1;
836
+ }
837
+ function getLeftmost(start) {
838
+ let p = start, leftmost = start;
839
+ do {
840
+ if (p.x < leftmost.x || p.x === leftmost.x && p.y < leftmost.y) leftmost = p;
841
+ p = p.next;
842
+ } while (p !== start);
843
+ return leftmost;
844
+ }
845
+ function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
846
+ return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && (ax - px) * (by - py) >= (bx - px) * (ay - py) && (bx - px) * (cy - py) >= (cx - px) * (by - py);
847
+ }
848
+ function isValidDiagonal(a, b) {
849
+ return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones't intersect other edges
850
+ (locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
851
+ (area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors
852
+ equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0);
853
+ }
854
+ function area(p, q, r) {
855
+ return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
856
+ }
857
+ function equals(p1, p2) {
858
+ return p1.x === p2.x && p1.y === p2.y;
859
+ }
860
+ function intersects(p1, q1, p2, q2) {
861
+ const o1 = sign(area(p1, q1, p2));
862
+ const o2 = sign(area(p1, q1, q2));
863
+ const o3 = sign(area(p2, q2, p1));
864
+ const o4 = sign(area(p2, q2, q1));
865
+ if (o1 !== o2 && o3 !== o4) return true;
866
+ if (o1 === 0 && onSegment(p1, p2, q1)) return true;
867
+ if (o2 === 0 && onSegment(p1, q2, q1)) return true;
868
+ if (o3 === 0 && onSegment(p2, p1, q2)) return true;
869
+ if (o4 === 0 && onSegment(p2, q1, q2)) return true;
870
+ return false;
871
+ }
872
+ function onSegment(p, q, r) {
873
+ return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y);
874
+ }
875
+ function sign(num2) {
876
+ return num2 > 0 ? 1 : num2 < 0 ? -1 : 0;
877
+ }
878
+ function intersectsPolygon(a, b) {
879
+ let p = a;
880
+ do {
881
+ if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b)) return true;
882
+ p = p.next;
883
+ } while (p !== a);
884
+ return false;
885
+ }
886
+ function locallyInside(a, b) {
887
+ return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0;
888
+ }
889
+ function middleInside(a, b) {
890
+ let p = a, inside = false;
891
+ const px = (a.x + b.x) / 2, py = (a.y + b.y) / 2;
892
+ do {
893
+ if (p.y > py !== p.next.y > py && p.next.y !== p.y && px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x)
894
+ inside = !inside;
895
+ p = p.next;
896
+ } while (p !== a);
897
+ return inside;
898
+ }
899
+ function splitPolygon(a, b) {
900
+ const a2 = new Node(a.i, a.x, a.y), b2 = new Node(b.i, b.x, b.y), an = a.next, bp = b.prev;
901
+ a.next = b;
902
+ b.prev = a;
903
+ a2.next = an;
904
+ an.prev = a2;
905
+ b2.next = a2;
906
+ a2.prev = b2;
907
+ bp.next = b2;
908
+ b2.prev = bp;
909
+ return b2;
910
+ }
911
+ function insertNode(i, x, y, last) {
912
+ const p = new Node(i, x, y);
913
+ if (!last) {
914
+ p.prev = p;
915
+ p.next = p;
916
+ } else {
917
+ p.next = last.next;
918
+ p.prev = last;
919
+ last.next.prev = p;
920
+ last.next = p;
921
+ }
922
+ return p;
923
+ }
924
+ function removeNode(p) {
925
+ p.next.prev = p.prev;
926
+ p.prev.next = p.next;
927
+ if (p.prevZ) p.prevZ.nextZ = p.nextZ;
928
+ if (p.nextZ) p.nextZ.prevZ = p.prevZ;
929
+ }
930
+ function Node(i, x, y) {
931
+ this.i = i;
932
+ this.x = x;
933
+ this.y = y;
934
+ this.prev = null;
935
+ this.next = null;
936
+ this.z = 0;
937
+ this.prevZ = null;
938
+ this.nextZ = null;
939
+ this.steiner = false;
940
+ }
941
+ function signedArea(data, start, end, dim) {
942
+ let sum = 0;
943
+ for (let i = start, j = end - dim; i < end; i += dim) {
944
+ sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
945
+ j = i;
946
+ }
947
+ return sum;
948
+ }
949
+ var equals2 = (a, b) => a.x === b.x && a.y === b.y;
950
+ var linePoint = (v1, v2, t) => t === 1 ? { x: v2.x, y: v2.y } : { x: (v2.x - v1.x) * t + v1.x, y: (v2.y - v1.y) * t + v1.y };
951
+ function pathPoints(points) {
952
+ const out = [];
953
+ let last;
954
+ for (let i = 1; i < points.length; i++) {
955
+ const v1 = { x: points[i - 1][0], y: points[i - 1][1] };
956
+ const v2 = { x: points[i][0], y: points[i][1] };
957
+ for (let d = 0; d <= 1; d++) {
958
+ const point = linePoint(v1, v2, d / 1);
959
+ if (last && equals2(last, point)) continue;
960
+ out.push(point);
961
+ last = point;
962
+ }
963
+ }
964
+ return out;
965
+ }
966
+ function area2(contour) {
967
+ const n = contour.length;
968
+ let a = 0;
969
+ for (let p = n - 1, q = 0; q < n; p = q++) a += contour[p].x * contour[q].y - contour[q].x * contour[p].y;
970
+ return a * 0.5;
971
+ }
972
+ var isClockWise = (pts) => area2(pts) < 0;
973
+ function removeDupEndPts(points) {
974
+ const l = points.length;
975
+ if (l > 2 && equals2(points[l - 1], points[0])) points.pop();
976
+ }
977
+ function triangulateShape(contour, holes) {
978
+ const vertices = [];
979
+ const holeIndices = [];
980
+ const faces = [];
981
+ removeDupEndPts(contour);
982
+ for (const p of contour) vertices.push(p.x, p.y);
983
+ let holeIndex = contour.length;
984
+ holes.forEach(removeDupEndPts);
985
+ for (let i = 0; i < holes.length; i++) {
986
+ holeIndices.push(holeIndex);
987
+ holeIndex += holes[i].length;
988
+ for (const p of holes[i]) vertices.push(p.x, p.y);
989
+ }
990
+ const triangles = Earcut.triangulate(vertices, holeIndices);
991
+ for (let i = 0; i < triangles.length; i += 3) faces.push(triangles.slice(i, i + 3));
992
+ return faces;
993
+ }
994
+ function computeVertexNormals(positions, indices) {
995
+ const normals = new Float32Array(positions.length);
996
+ const get = (array, i) => [array[i * 3], array[i * 3 + 1], array[i * 3 + 2]];
997
+ const face = (vA, vB, vC) => {
998
+ const pA = get(positions, vA);
999
+ const pB = get(positions, vB);
1000
+ const pC = get(positions, vC);
1001
+ const cb = [pC[0] - pB[0], pC[1] - pB[1], pC[2] - pB[2]];
1002
+ const ab = [pA[0] - pB[0], pA[1] - pB[1], pA[2] - pB[2]];
1003
+ return [cb[1] * ab[2] - cb[2] * ab[1], cb[2] * ab[0] - cb[0] * ab[2], cb[0] * ab[1] - cb[1] * ab[0]];
1004
+ };
1005
+ const set = (i, x, y, z) => {
1006
+ normals[i * 3] = x;
1007
+ normals[i * 3 + 1] = y;
1008
+ normals[i * 3 + 2] = z;
1009
+ };
1010
+ if (indices) {
1011
+ for (let i = 0; i < indices.length; i += 3) {
1012
+ const vA = indices[i];
1013
+ const vB = indices[i + 1];
1014
+ const vC = indices[i + 2];
1015
+ const cb = face(vA, vB, vC);
1016
+ for (const v of [vA, vB, vC]) {
1017
+ const n = get(normals, v);
1018
+ set(v, n[0] + cb[0], n[1] + cb[1], n[2] + cb[2]);
1019
+ }
1020
+ }
1021
+ } else {
1022
+ for (let i = 0; i < positions.length / 3; i += 3) {
1023
+ const cb = face(i, i + 1, i + 2);
1024
+ set(i, cb[0], cb[1], cb[2]);
1025
+ set(i + 1, cb[0], cb[1], cb[2]);
1026
+ set(i + 2, cb[0], cb[1], cb[2]);
1027
+ }
1028
+ }
1029
+ for (let i = 0; i < normals.length / 3; i++) {
1030
+ const [x, y, z] = get(normals, i);
1031
+ const s = 1 / (Math.sqrt(x * x + y * y + z * z) || 1);
1032
+ set(i, x * s, y * s, z * s);
1033
+ }
1034
+ return normals;
1035
+ }
1036
+ function extrude(outline, depth = 1, steps = 1, bevel = void 0) {
1037
+ const parameters = { shape: outline, depth, steps, bevel: bevel ?? {} };
1038
+ const verticesArray = [];
1039
+ const uvArray = [];
1040
+ const groups = [];
1041
+ const placeholder = [];
1042
+ const bevelEnabled = bevel !== false;
1043
+ const spec = bevel === false || bevel === void 0 ? {} : bevel;
1044
+ let bevelThickness = spec.thickness !== void 0 ? spec.thickness : 0.2;
1045
+ let bevelSize = spec.size !== void 0 ? spec.size : bevelThickness - 0.1;
1046
+ let bevelOffset = spec.offset !== void 0 ? spec.offset : 0;
1047
+ let bevelSegments = spec.segments !== void 0 ? spec.segments : 3;
1048
+ if (!bevelEnabled) {
1049
+ bevelSegments = 0;
1050
+ bevelThickness = 0;
1051
+ bevelSize = 0;
1052
+ bevelOffset = 0;
1053
+ }
1054
+ let vertices = pathPoints(outline.outer);
1055
+ const holes = (outline.holes ?? []).map(pathPoints);
1056
+ const reverse = !isClockWise(vertices);
1057
+ if (reverse) {
1058
+ vertices = vertices.reverse();
1059
+ for (let h = 0; h < holes.length; h++) {
1060
+ const ahole = holes[h];
1061
+ if (isClockWise(ahole)) holes[h] = ahole.reverse();
1062
+ }
1063
+ }
1064
+ const faces = triangulateShape(vertices, holes);
1065
+ const contour = vertices;
1066
+ for (const ahole of holes) vertices = vertices.concat(ahole);
1067
+ const scalePt2 = (pt, vec, size) => ({ x: pt.x + vec.x * size, y: pt.y + vec.y * size });
1068
+ const vlen = vertices.length;
1069
+ const flen = faces.length;
1070
+ const getBevelVec = (inPt, inPrev, inNext) => {
1071
+ let vTransX;
1072
+ let vTransY;
1073
+ let shrinkBy;
1074
+ const vPrevX = inPt.x - inPrev.x;
1075
+ const vPrevY = inPt.y - inPrev.y;
1076
+ const vNextX = inNext.x - inPt.x;
1077
+ const vNextY = inNext.y - inPt.y;
1078
+ const vPrevLensq = vPrevX * vPrevX + vPrevY * vPrevY;
1079
+ const collinear0 = vPrevX * vNextY - vPrevY * vNextX;
1080
+ if (Math.abs(collinear0) > Number.EPSILON) {
1081
+ const vPrevLen = Math.sqrt(vPrevLensq);
1082
+ const vNextLen = Math.sqrt(vNextX * vNextX + vNextY * vNextY);
1083
+ const ptPrevShiftX = inPrev.x - vPrevY / vPrevLen;
1084
+ const ptPrevShiftY = inPrev.y + vPrevX / vPrevLen;
1085
+ const ptNextShiftX = inNext.x - vNextY / vNextLen;
1086
+ const ptNextShiftY = inNext.y + vNextX / vNextLen;
1087
+ const sf = ((ptNextShiftX - ptPrevShiftX) * vNextY - (ptNextShiftY - ptPrevShiftY) * vNextX) / (vPrevX * vNextY - vPrevY * vNextX);
1088
+ vTransX = ptPrevShiftX + vPrevX * sf - inPt.x;
1089
+ vTransY = ptPrevShiftY + vPrevY * sf - inPt.y;
1090
+ const vTransLensq = vTransX * vTransX + vTransY * vTransY;
1091
+ if (vTransLensq <= 2) return { x: vTransX, y: vTransY };
1092
+ shrinkBy = Math.sqrt(vTransLensq / 2);
1093
+ } else {
1094
+ let directionEq = false;
1095
+ if (vPrevX > Number.EPSILON) {
1096
+ if (vNextX > Number.EPSILON) directionEq = true;
1097
+ } else if (vPrevX < -Number.EPSILON) {
1098
+ if (vNextX < -Number.EPSILON) directionEq = true;
1099
+ } else if (Math.sign(vPrevY) === Math.sign(vNextY)) {
1100
+ directionEq = true;
1101
+ }
1102
+ if (directionEq) {
1103
+ vTransX = -vPrevY;
1104
+ vTransY = vPrevX;
1105
+ shrinkBy = Math.sqrt(vPrevLensq);
1106
+ } else {
1107
+ vTransX = vPrevX;
1108
+ vTransY = vPrevY;
1109
+ shrinkBy = Math.sqrt(vPrevLensq / 2);
1110
+ }
1111
+ }
1112
+ return { x: vTransX / shrinkBy, y: vTransY / shrinkBy };
1113
+ };
1114
+ const movements = (ring2) => {
1115
+ const out = [];
1116
+ for (let i = 0, il = ring2.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) {
1117
+ if (j === il) j = 0;
1118
+ if (k === il) k = 0;
1119
+ out[i] = getBevelVec(ring2[i], ring2[j], ring2[k]);
1120
+ }
1121
+ return out;
1122
+ };
1123
+ const contourMovements = movements(contour);
1124
+ const holesMovements = [];
1125
+ let verticesMovements = contourMovements.concat();
1126
+ for (const ahole of holes) {
1127
+ const oneHoleMovements = movements(ahole);
1128
+ holesMovements.push(oneHoleMovements);
1129
+ verticesMovements = verticesMovements.concat(oneHoleMovements);
1130
+ }
1131
+ const v = (x, y, z) => {
1132
+ placeholder.push(x);
1133
+ placeholder.push(y);
1134
+ placeholder.push(z);
1135
+ };
1136
+ const bevelLayer = (b, zOf) => {
1137
+ const t = b / bevelSegments;
1138
+ const z = bevelThickness * Math.cos(t * Math.PI / 2);
1139
+ const bs2 = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset;
1140
+ for (let i = 0; i < contour.length; i++) {
1141
+ const vert = scalePt2(contour[i], contourMovements[i], bs2);
1142
+ v(vert.x, vert.y, zOf(z));
1143
+ }
1144
+ for (let h = 0; h < holes.length; h++) {
1145
+ const ahole = holes[h];
1146
+ const oneHoleMovements = holesMovements[h];
1147
+ for (let i = 0; i < ahole.length; i++) {
1148
+ const vert = scalePt2(ahole[i], oneHoleMovements[i], bs2);
1149
+ v(vert.x, vert.y, zOf(z));
1150
+ }
1151
+ }
1152
+ };
1153
+ for (let b = 0; b < bevelSegments; b++) bevelLayer(b, (z) => -z);
1154
+ const bs = bevelSize + bevelOffset;
1155
+ for (let i = 0; i < vlen; i++) {
1156
+ const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i];
1157
+ v(vert.x, vert.y, 0);
1158
+ }
1159
+ for (let s = 1; s <= steps; s++) {
1160
+ for (let i = 0; i < vlen; i++) {
1161
+ const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i];
1162
+ v(vert.x, vert.y, depth / steps * s);
1163
+ }
1164
+ }
1165
+ for (let b = bevelSegments - 1; b >= 0; b--) bevelLayer(b, (z) => depth + z);
1166
+ const addVertex = (index) => {
1167
+ verticesArray.push(placeholder[index * 3 + 0]);
1168
+ verticesArray.push(placeholder[index * 3 + 1]);
1169
+ verticesArray.push(placeholder[index * 3 + 2]);
1170
+ };
1171
+ const addUV = (x, y) => {
1172
+ uvArray.push(x);
1173
+ uvArray.push(y);
1174
+ };
1175
+ const at = (index, axis) => verticesArray[index * 3 + axis];
1176
+ const f3 = (a, b, c) => {
1177
+ addVertex(a);
1178
+ addVertex(b);
1179
+ addVertex(c);
1180
+ const nextIndex = verticesArray.length / 3;
1181
+ for (const index of [nextIndex - 3, nextIndex - 2, nextIndex - 1]) addUV(at(index, 0), at(index, 1));
1182
+ };
1183
+ const f4 = (a, b, c, d) => {
1184
+ addVertex(a);
1185
+ addVertex(b);
1186
+ addVertex(d);
1187
+ addVertex(b);
1188
+ addVertex(c);
1189
+ addVertex(d);
1190
+ const nextIndex = verticesArray.length / 3;
1191
+ const iA = nextIndex - 6;
1192
+ const iB = nextIndex - 3;
1193
+ const iC = nextIndex - 2;
1194
+ const iD = nextIndex - 1;
1195
+ const byX = Math.abs(at(iA, 1) - at(iB, 1)) < Math.abs(at(iA, 0) - at(iB, 0));
1196
+ const uv = (index) => [byX ? at(index, 0) : at(index, 1), 1 - at(index, 2)];
1197
+ const uvs = [uv(iA), uv(iB), uv(iC), uv(iD)];
1198
+ for (const k of [0, 1, 3, 1, 2, 3]) addUV(...uvs[k]);
1199
+ };
1200
+ let start = verticesArray.length / 3;
1201
+ if (bevelEnabled) {
1202
+ let offset = 0;
1203
+ for (let i = 0; i < flen; i++) {
1204
+ const face = faces[i];
1205
+ f3(face[2] + offset, face[1] + offset, face[0] + offset);
1206
+ }
1207
+ const layer = steps + bevelSegments * 2;
1208
+ offset = vlen * layer;
1209
+ for (let i = 0; i < flen; i++) {
1210
+ const face = faces[i];
1211
+ f3(face[0] + offset, face[1] + offset, face[2] + offset);
1212
+ }
1213
+ } else {
1214
+ for (let i = 0; i < flen; i++) {
1215
+ const face = faces[i];
1216
+ f3(face[2], face[1], face[0]);
1217
+ }
1218
+ for (let i = 0; i < flen; i++) {
1219
+ const face = faces[i];
1220
+ f3(face[0] + vlen * steps, face[1] + vlen * steps, face[2] + vlen * steps);
1221
+ }
1222
+ }
1223
+ groups.push({ start, count: verticesArray.length / 3 - start, materialIndex: 0 });
1224
+ start = verticesArray.length / 3;
1225
+ const sidewalls = (ring2, layeroffset2) => {
1226
+ let i = ring2.length;
1227
+ while (--i >= 0) {
1228
+ const j = i;
1229
+ let k = i - 1;
1230
+ if (k < 0) k = ring2.length - 1;
1231
+ for (let s = 0, sl = steps + bevelSegments * 2; s < sl; s++) {
1232
+ const slen1 = vlen * s;
1233
+ const slen2 = vlen * (s + 1);
1234
+ f4(layeroffset2 + j + slen1, layeroffset2 + k + slen1, layeroffset2 + k + slen2, layeroffset2 + j + slen2);
1235
+ }
1236
+ }
1237
+ };
1238
+ let layeroffset = 0;
1239
+ sidewalls(contour, layeroffset);
1240
+ layeroffset += contour.length;
1241
+ for (const ahole of holes) {
1242
+ sidewalls(ahole, layeroffset);
1243
+ layeroffset += ahole.length;
1244
+ }
1245
+ groups.push({ start, count: verticesArray.length / 3 - start, materialIndex: 1 });
1246
+ const positions = new Float32Array(verticesArray);
1247
+ return { type: "ExtrudeGeometry", parameters, positions, normals: computeVertexNormals(positions), uvs: new Float32Array(uvArray), groups };
1248
+ }
1249
+ var PARAMETER_KEYS = {
1250
+ box: ["size", "segments"],
1251
+ cylinder: ["radiusTop", "radiusBottom", "height", "radialSegments", "heightSegments", "openEnded", "thetaStart", "thetaLength"],
1252
+ cone: ["radius", "height", "radialSegments", "heightSegments", "openEnded", "thetaStart", "thetaLength"],
1253
+ sphere: ["radius", "widthSegments", "heightSegments", "phiStart", "phiLength", "thetaStart", "thetaLength"],
1254
+ plane: ["width", "height", "widthSegments", "heightSegments"],
1255
+ circle: ["radius", "segments", "thetaStart", "thetaLength"],
1256
+ ring: ["innerRadius", "outerRadius", "thetaSegments", "phiSegments", "thetaStart", "thetaLength"],
1257
+ lathe: ["points", "segments", "phiStart", "phiLength"],
1258
+ torus: ["radius", "tube", "radialSegments", "tubularSegments", "arc"],
1259
+ extrude: ["shape", "depth", "steps", "bevel"],
1260
+ difference: ["size", "subtract"],
1261
+ mesh: ["positions", "normals", "uvs", "indices"]
1262
+ };
1263
+ var SEGMENT_KEYS = {
1264
+ cylinder: ["radialSegments"],
1265
+ cone: ["radialSegments"],
1266
+ sphere: ["widthSegments", "heightSegments"],
1267
+ circle: ["segments"],
1268
+ ring: ["thetaSegments"],
1269
+ lathe: ["segments"],
1270
+ torus: ["radialSegments", "tubularSegments"]
1271
+ };
1272
+ var SEGMENT_DEFAULTS = { radialSegments: 32, widthSegments: 32, heightSegments: 16, segments: 12, thetaSegments: 32, tubularSegments: 48 };
1273
+ var segmentDefault = (kind, key) => kind === "circle" && key === "segments" ? 32 : kind === "torus" && key === "radialSegments" ? 12 : SEGMENT_DEFAULTS[key];
1274
+ function primitiveParameters(node, options = {}) {
1275
+ const out = {};
1276
+ const source = node;
1277
+ for (const key of PARAMETER_KEYS[node.kind]) if (source[key] !== void 0) out[key] = source[key];
1278
+ const detail = options.detail ?? 1;
1279
+ if (detail !== 1) {
1280
+ const floor = options.minSegments ?? 3;
1281
+ for (const key of SEGMENT_KEYS[node.kind] ?? []) {
1282
+ const given = out[key] ?? segmentDefault(node.kind, key);
1283
+ out[key] = Math.max(floor, Math.round(given * detail));
1284
+ }
1285
+ }
1286
+ return out;
1287
+ }
1288
+ function mesh(p) {
1289
+ const positions = new Float32Array(p.positions);
1290
+ const indices = p.indices ? indexArray(p.indices) : void 0;
1291
+ return {
1292
+ type: "BufferGeometry",
1293
+ parameters: {},
1294
+ positions,
1295
+ normals: p.normals ? new Float32Array(p.normals) : computeVertexNormals(positions, indices),
1296
+ uvs: p.uvs ? new Float32Array(p.uvs) : new Float32Array(positions.length / 3 * 2),
1297
+ ...indices ? { indices } : {},
1298
+ groups: []
1299
+ };
1300
+ }
1301
+ function make(kind, p) {
1302
+ switch (kind) {
1303
+ case "box": {
1304
+ const size = p.size ?? [1, 1, 1];
1305
+ const segments = p.segments ?? [1, 1, 1];
1306
+ return box(size[0], size[1], size[2], segments[0], segments[1], segments[2]);
1307
+ }
1308
+ case "cylinder":
1309
+ return cylinder(p.radiusTop, p.radiusBottom, p.height, p.radialSegments, p.heightSegments, p.openEnded, p.thetaStart, p.thetaLength);
1310
+ case "cone":
1311
+ return cone(p.radius, p.height, p.radialSegments, p.heightSegments, p.openEnded, p.thetaStart, p.thetaLength);
1312
+ case "sphere":
1313
+ return sphere(p.radius, p.widthSegments, p.heightSegments, p.phiStart, p.phiLength, p.thetaStart, p.thetaLength);
1314
+ case "plane":
1315
+ return plane(p.width, p.height, p.widthSegments, p.heightSegments);
1316
+ case "circle":
1317
+ return circle(p.radius, p.segments, p.thetaStart, p.thetaLength);
1318
+ case "ring":
1319
+ return ring(p.innerRadius, p.outerRadius, p.thetaSegments, p.phiSegments, p.thetaStart, p.thetaLength);
1320
+ case "lathe":
1321
+ return lathe(p.points, p.segments, p.phiStart, p.phiLength);
1322
+ case "torus":
1323
+ return torus(p.radius, p.tube, p.radialSegments, p.tubularSegments, p.arc);
1324
+ case "extrude":
1325
+ return extrude(p.shape, p.depth, p.steps, p.bevel);
1326
+ case "difference":
1327
+ return difference(p.size, p.subtract ?? []);
1328
+ case "mesh":
1329
+ return mesh(p);
1330
+ default:
1331
+ throw new Error(`game-shapes: unknown primitive kind "${String(kind)}"`);
1332
+ }
1333
+ }
1334
+ var templates = /* @__PURE__ */ new Map();
1335
+ var templateLimit = 4096;
1336
+ var stats = { hits: 0, misses: 0 };
1337
+ function buildPrimitive(node, options = {}) {
1338
+ const parameters = primitiveParameters(node, options);
1339
+ if (node.kind === "mesh" || templateLimit === 0) return make(node.kind, parameters);
1340
+ const key = `${node.kind}:${canonicalJson(parameters)}`;
1341
+ const cached = templates.get(key);
1342
+ if (cached) {
1343
+ stats.hits++;
1344
+ templates.delete(key);
1345
+ templates.set(key, cached);
1346
+ return cached;
1347
+ }
1348
+ stats.misses++;
1349
+ const built = make(node.kind, parameters);
1350
+ templates.set(key, built);
1351
+ if (templates.size > templateLimit) templates.delete(templates.keys().next().value);
1352
+ return built;
1353
+ }
1354
+ var num = (v) => typeof v === "number" && Number.isFinite(v) ? null : "expected a finite number";
1355
+ var bool = (v) => typeof v === "boolean" ? null : "expected a boolean";
1356
+ var str = (v) => typeof v === "string" ? null : "expected a string";
1357
+ var int = (min) => (v) => typeof v === "number" && Number.isInteger(v) && v >= min ? null : `expected an integer >= ${min}`;
1358
+ var range = (lo, hi, loOpen = false) => (v) => typeof v === "number" && (loOpen ? v > lo : v >= lo) && v <= hi ? null : `expected a number in ${loOpen ? "(" : "["}${lo}, ${hi}]`;
1359
+ var tuple = (n, item = num) => (v) => {
1360
+ if (!Array.isArray(v) || v.length !== n) return `expected an array of ${n}`;
1361
+ for (const x of v) if (item(x)) return `expected an array of ${n} numbers`;
1362
+ return null;
1363
+ };
1364
+ var list = (item, min = 0) => (v) => {
1365
+ if (!Array.isArray(v)) return "expected an array";
1366
+ if (v.length < min) return `expected at least ${min} items`;
1367
+ for (let i = 0; i < v.length; i++) {
1368
+ const e = item(v[i]);
1369
+ if (e) return `[${i}]: ${e}`;
1370
+ }
1371
+ return null;
1372
+ };
1373
+ var color = (v) => typeof v === "number" && Number.isInteger(v) && v >= 0 && v <= 16777215 || typeof v === "string" && /^#[0-9a-fA-F]{6}$/.test(v) ? null : 'expected 0xRRGGBB or "#rrggbb"';
1374
+ var oneOf = (values) => (v) => values.includes(v) ? null : `expected one of ${values.map((x) => JSON.stringify(x)).join(", ")}`;
1375
+ var object = (fields, required = []) => (v) => {
1376
+ if (!v || typeof v !== "object" || Array.isArray(v)) return "expected an object";
1377
+ const record = v;
1378
+ for (const key of required) if (record[key] === void 0) return `${key}: required`;
1379
+ for (const key of Object.keys(record)) {
1380
+ if (record[key] === void 0) continue;
1381
+ const check = fields[key];
1382
+ if (!check) return `${key}: unknown field`;
1383
+ const e = check(record[key]);
1384
+ if (e) return `${key}: ${e}`;
1385
+ }
1386
+ return null;
1387
+ };
1388
+ var ORDERS = ["XYZ", "YXZ", "ZXY", "ZYX", "YZX", "XZY"];
1389
+ var TRANSFORM = {
1390
+ position: tuple(3),
1391
+ rotation: tuple(3),
1392
+ rotationOrder: oneOf(ORDERS),
1393
+ scale: (v) => typeof v === "number" ? num(v) : tuple(3)(v),
1394
+ heading: num,
1395
+ matrix: tuple(16)
1396
+ };
1397
+ var UV = object({ u0: num, v0: num, u1: num, v1: num }, ["u0", "v0", "u1", "v1"]);
1398
+ var INSTANCE = object({ ...TRANSFORM, color });
1399
+ var COMMON = {
1400
+ ...TRANSFORM,
1401
+ kind: str,
1402
+ name: str,
1403
+ part: str,
1404
+ material: str,
1405
+ color,
1406
+ uv: UV,
1407
+ instances: list(INSTANCE),
1408
+ minDetail: range(0, 1, true),
1409
+ tags: list(str)
1410
+ };
1411
+ var vec2 = tuple(2);
1412
+ var BEVEL = (v) => v === false ? null : object({ thickness: num, size: num, offset: num, segments: int(1) })(v);
1413
+ var KINDS = {
1414
+ group: { fields: {} },
1415
+ box: { fields: { size: tuple(3), segments: tuple(3, int(1)) } },
1416
+ cylinder: { fields: { radiusTop: num, radiusBottom: num, height: num, radialSegments: int(1), heightSegments: int(1), openEnded: bool, thetaStart: num, thetaLength: num } },
1417
+ cone: { fields: { radius: num, height: num, radialSegments: int(1), heightSegments: int(1), openEnded: bool, thetaStart: num, thetaLength: num } },
1418
+ sphere: { fields: { radius: num, widthSegments: int(3), heightSegments: int(2), phiStart: num, phiLength: num, thetaStart: num, thetaLength: num } },
1419
+ plane: { fields: { width: num, height: num, widthSegments: int(1), heightSegments: int(1) } },
1420
+ circle: { fields: { radius: num, segments: int(3), thetaStart: num, thetaLength: num } },
1421
+ ring: { fields: { innerRadius: num, outerRadius: num, thetaSegments: int(3), phiSegments: int(1), thetaStart: num, thetaLength: num } },
1422
+ lathe: { fields: { points: list(vec2, 2), segments: int(1), phiStart: num, phiLength: num }, required: ["points"] },
1423
+ torus: { fields: { radius: num, tube: num, radialSegments: int(1), tubularSegments: int(1), arc: num } },
1424
+ extrude: {
1425
+ fields: { shape: object({ outer: list(vec2, 3), holes: list(list(vec2, 3)) }, ["outer"]), depth: num, steps: int(1), bevel: BEVEL },
1426
+ required: ["shape"]
1427
+ },
1428
+ difference: { fields: { size: tuple(3), subtract: list(object({ min: vec2, max: vec2 }, ["min", "max"])) }, required: ["size", "subtract"] },
1429
+ mesh: {
1430
+ fields: {
1431
+ positions: (v) => list(num)(v) ?? (v.length % 3 === 0 ? null : "expected a multiple of 3 numbers"),
1432
+ normals: list(num),
1433
+ uvs: list(num),
1434
+ indices: list(int(0))
1435
+ },
1436
+ required: ["positions"]
1437
+ },
1438
+ point: { fields: { data: () => null } },
1439
+ custom: { fields: { generator: str, params: () => null }, required: ["generator"] }
1440
+ };
1441
+
1442
+ // ../game-shapes-three/dist/index.js
1443
+ var UV_BEFORE_NORMAL = /* @__PURE__ */ new Set(["LatheGeometry", "ExtrudeGeometry"]);
1444
+ var INDEX_FIRST = /* @__PURE__ */ new Set(["BoxGeometry", "CylinderGeometry", "ConeGeometry", "SphereGeometry", "PlaneGeometry", "CircleGeometry", "RingGeometry", "LatheGeometry", "TorusGeometry"]);
1445
+ var isPrimitive = (value) => typeof value.type === "string";
1446
+ function threeParameters(THREE, p) {
1447
+ if (p.type === "LatheGeometry") {
1448
+ const points = p.parameters.points.map(({ x, y }) => new THREE.Vector2(x, y));
1449
+ return { ...p.parameters, points };
1450
+ }
1451
+ return { ...p.parameters };
1452
+ }
1453
+ function toGeometry(THREE, source, options = {}) {
1454
+ const copy = options.copy !== false;
1455
+ const primitive = isPrimitive(source) ? source : null;
1456
+ const ThreeClass = primitive && primitive.type !== "BufferGeometry" ? THREE[primitive.type] : void 0;
1457
+ const geometry = ThreeClass ? Reflect.construct(THREE.BufferGeometry, [], ThreeClass) : new THREE.BufferGeometry();
1458
+ if (primitive && ThreeClass) {
1459
+ Object.assign(geometry, { type: primitive.type, parameters: threeParameters(THREE, primitive) });
1460
+ }
1461
+ const attribute = (array, size) => copy ? new THREE.Float32BufferAttribute(array, size) : new THREE.BufferAttribute(array, size);
1462
+ const index = source.indices;
1463
+ const setIndex = () => {
1464
+ if (!index) return;
1465
+ geometry.setIndex(copy ? index instanceof Uint32Array ? new THREE.Uint32BufferAttribute(index, 1) : new THREE.Uint16BufferAttribute(index, 1) : new THREE.BufferAttribute(index, 1));
1466
+ };
1467
+ const type = primitive?.type ?? "BufferGeometry";
1468
+ if (INDEX_FIRST.has(type)) setIndex();
1469
+ geometry.setAttribute("position", attribute(source.positions, 3));
1470
+ if (UV_BEFORE_NORMAL.has(type)) {
1471
+ geometry.setAttribute("uv", attribute(source.uvs, 2));
1472
+ geometry.setAttribute("normal", attribute(source.normals, 3));
1473
+ } else {
1474
+ geometry.setAttribute("normal", attribute(source.normals, 3));
1475
+ geometry.setAttribute("uv", attribute(source.uvs, 2));
1476
+ }
1477
+ if (!primitive && source.colors) geometry.setAttribute("color", attribute(source.colors, 3));
1478
+ if (!INDEX_FIRST.has(type)) setIndex();
1479
+ if (primitive) for (const group of primitive.groups) geometry.addGroup(group.start, group.count, group.materialIndex);
1480
+ return geometry;
1481
+ }
1482
+ var SHAPE_GEOMETRY_TYPES = ["BoxGeometry", "CylinderGeometry", "ConeGeometry", "SphereGeometry", "PlaneGeometry", "CircleGeometry", "RingGeometry", "LatheGeometry", "TorusGeometry", "ExtrudeGeometry"];
1483
+ function linePoints(path) {
1484
+ if (path.autoClose || path.curves.length === 0) return null;
1485
+ const points = [];
1486
+ for (const [index, curve] of path.curves.entries()) {
1487
+ if (!curve.isLineCurve || !curve.v1 || !curve.v2) return null;
1488
+ if (index === 0) points.push([curve.v1.x, curve.v1.y]);
1489
+ else {
1490
+ const last = points[points.length - 1];
1491
+ if (last[0] !== curve.v1.x || last[1] !== curve.v1.y) return null;
1492
+ }
1493
+ points.push([curve.v2.x, curve.v2.y]);
1494
+ }
1495
+ return points;
1496
+ }
1497
+ function extrudeOf(shapes, options = {}) {
1498
+ if (Array.isArray(shapes) || !shapes || options.extrudePath !== void 0 || options.UVGenerator !== void 0) return null;
1499
+ const shape = shapes;
1500
+ const outer = linePoints(shape);
1501
+ if (!outer) return null;
1502
+ const holes = [];
1503
+ for (const hole of shape.holes ?? []) {
1504
+ const points = linePoints(hole);
1505
+ if (!points) return null;
1506
+ holes.push(points);
1507
+ }
1508
+ const outline = holes.length ? { outer, holes } : { outer };
1509
+ const bevel = options.bevelEnabled === false ? false : {
1510
+ ...options.bevelThickness !== void 0 ? { thickness: options.bevelThickness } : {},
1511
+ ...options.bevelSize !== void 0 ? { size: options.bevelSize } : {},
1512
+ ...options.bevelOffset !== void 0 ? { offset: options.bevelOffset } : {},
1513
+ ...options.bevelSegments !== void 0 ? { segments: options.bevelSegments } : {}
1514
+ };
1515
+ return extrude(outline, options.depth, options.steps, bevel);
1516
+ }
1517
+ var namespaces = /* @__PURE__ */ new WeakMap();
1518
+ var shims = /* @__PURE__ */ new WeakSet();
1519
+ function shapeGeometries(THREE, options = {}) {
1520
+ if (shims.has(THREE)) return THREE;
1521
+ const cache = options.cache !== false;
1522
+ const key = cache ? "cache" : "nocache";
1523
+ let byKey = namespaces.get(THREE);
1524
+ if (!byKey) {
1525
+ byKey = /* @__PURE__ */ new Map();
1526
+ namespaces.set(THREE, byKey);
1527
+ }
1528
+ const existing = byKey.get(key);
1529
+ if (existing) return existing;
1530
+ const made = (p) => toGeometry(THREE, p);
1531
+ const viaCache = (node, direct) => cache ? buildPrimitive(node) : direct();
1532
+ const defined = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
1533
+ const constructors = {
1534
+ BoxGeometry: (width, height, depth, ws = 1, hs = 1, ds = 1) => made(viaCache({ kind: "box", size: [width ?? 1, height ?? 1, depth ?? 1], segments: [ws, hs, ds] }, () => box(width, height, depth, ws, hs, ds))),
1535
+ CylinderGeometry: (radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) => made(viaCache({ kind: "cylinder", ...defined({ radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength }) }, () => cylinder(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength))),
1536
+ ConeGeometry: (radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) => made(viaCache({ kind: "cone", ...defined({ radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength }) }, () => cone(radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength))),
1537
+ SphereGeometry: (radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength) => made(viaCache({ kind: "sphere", ...defined({ radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength }) }, () => sphere(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength))),
1538
+ PlaneGeometry: (width, height, widthSegments, heightSegments) => made(viaCache({ kind: "plane", ...defined({ width, height, widthSegments, heightSegments }) }, () => plane(width, height, widthSegments, heightSegments))),
1539
+ CircleGeometry: (radius, segments, thetaStart, thetaLength) => made(viaCache({ kind: "circle", ...defined({ radius, segments, thetaStart, thetaLength }) }, () => circle(radius, segments, thetaStart, thetaLength))),
1540
+ RingGeometry: (innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength) => made(viaCache({ kind: "ring", ...defined({ innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength }) }, () => ring(innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength))),
1541
+ LatheGeometry: (points, segments, phiStart, phiLength) => {
1542
+ if (!points) return new THREE.LatheGeometry(void 0, segments, phiStart, phiLength);
1543
+ const profile = points.map((p) => [p.x, p.y]);
1544
+ return made(viaCache({ kind: "lathe", points: profile, ...defined({ segments, phiStart, phiLength }) }, () => lathe(profile, segments, phiStart, phiLength)));
1545
+ },
1546
+ TorusGeometry: (radius, tube, radialSegments, tubularSegments, arc) => made(viaCache({ kind: "torus", ...defined({ radius, tube, radialSegments, tubularSegments, arc }) }, () => torus(radius, tube, radialSegments, tubularSegments, arc))),
1547
+ ExtrudeGeometry: (shapes, extrudeOptions) => {
1548
+ const buffers = shapes === void 0 ? null : extrudeOf(shapes, extrudeOptions);
1549
+ if (!buffers) return new THREE.ExtrudeGeometry(shapes, extrudeOptions);
1550
+ const geometry = toGeometry(THREE, buffers);
1551
+ geometry.parameters = { shapes, options: extrudeOptions ?? {} };
1552
+ return geometry;
1553
+ }
1554
+ };
1555
+ const namespace = { ...THREE };
1556
+ for (const type of SHAPE_GEOMETRY_TYPES) {
1557
+ const make2 = constructors[type];
1558
+ const Constructor = function(...args) {
1559
+ return make2(...args);
1560
+ };
1561
+ Constructor.prototype = THREE[type].prototype;
1562
+ Object.defineProperty(Constructor, "name", { value: type });
1563
+ namespace[type] = Constructor;
1564
+ }
1565
+ const result = namespace;
1566
+ byKey.set(key, result);
1567
+ shims.add(result);
1568
+ return result;
1569
+ }
1570
+
50
1571
  // src/locales/en.json
51
1572
  var en_default = {
52
1573
  plate: {
@@ -742,9 +2263,9 @@ var GameVehicles = (() => {
742
2263
  return envTex;
743
2264
  }
744
2265
  kit.studioEnvironment = studioEnvironment;
745
- function makeMaterial(color, o) {
2266
+ function makeMaterial(color2, o) {
746
2267
  o = o || {};
747
- const c = color instanceof THREE.Color ? color : new THREE.Color(color);
2268
+ const c = color2 instanceof THREE.Color ? color2 : new THREE.Color(color2);
748
2269
  const key = "s" + c.getHexString() + JSON.stringify(o, function(k, v) {
749
2270
  return v && v.isColor ? "#" + v.getHexString() : v && v.isTexture ? v.uuid : v;
750
2271
  });
@@ -760,8 +2281,8 @@ var GameVehicles = (() => {
760
2281
  }
761
2282
  return cache[key];
762
2283
  }
763
- function makeFlatMaterial(color, o) {
764
- return makeMaterial(color, Object.assign({ flatShading: true, roughness: 0.92 }, o || {}));
2284
+ function makeFlatMaterial(color2, o) {
2285
+ return makeMaterial(color2, Object.assign({ flatShading: true, roughness: 0.92 }, o || {}));
765
2286
  }
766
2287
  const DOUBLE_SIDE = THREE.DoubleSide;
767
2288
  function toColor(hex) {
@@ -882,9 +2403,11 @@ var GameVehicles = (() => {
882
2403
  const V3 = THREE.Vector3;
883
2404
  const PI = Math.PI;
884
2405
  const I = kit._;
2406
+ const lod = kit.lod;
885
2407
  function roundedBoxGeometry(w, h, d, r) {
886
2408
  r = Math.max(2e-3, Math.min(r, w * 0.45, h * 0.45, d * 0.45));
887
- const nb = 3;
2409
+ if (lod.on && (r < 0.012 || Math.max(w, h, d) < lod.flatBoxEdge || Math.min(w, h, d) < 0.08)) return new THREE.BoxGeometry(w, h, d);
2410
+ const nb = lod.on ? lod.roundingRings : 3;
888
2411
  const n = 2 * nb + 1;
889
2412
  const g = new THREE.BoxGeometry(1, 1, 1, n, n, n);
890
2413
  const p = g.attributes.position;
@@ -929,6 +2452,7 @@ var GameVehicles = (() => {
929
2452
  geo.computeVertexNormals();
930
2453
  }
931
2454
  function subdivide(geo, maxLen) {
2455
+ if (lod.on) maxLen = maxLen * lod.subdivisionFactor;
932
2456
  const src = geo.index ? geo.toNonIndexed() : geo;
933
2457
  const a = src.attributes.position.array;
934
2458
  const out = [];
@@ -1006,10 +2530,10 @@ var GameVehicles = (() => {
1006
2530
  const s = new V3();
1007
2531
  for (let i = 0; i < n; i++) {
1008
2532
  const fi = i / 3 | 0;
1009
- const list = map[keys[i]];
2533
+ const list2 = map[keys[i]];
1010
2534
  s.set(0, 0, 0);
1011
- for (let j = 0; j < list.length; j++) {
1012
- const g = FN[list[j]];
2535
+ for (let j = 0; j < list2.length; j++) {
2536
+ const g = FN[list2[j]];
1013
2537
  if (g.dot(FN[fi]) >= cs) s.add(g);
1014
2538
  }
1015
2539
  if (s.lengthSq() < 1e-20) s.copy(FN[fi]);
@@ -1075,7 +2599,7 @@ var GameVehicles = (() => {
1075
2599
  const pos = [];
1076
2600
  const col = [];
1077
2601
  const idx = [];
1078
- const ring = seg + 1;
2602
+ const ring2 = seg + 1;
1079
2603
  st.forEach(function(s) {
1080
2604
  for (let j = 0; j <= seg; j++) {
1081
2605
  const a = j / seg * PI * 2;
@@ -1093,8 +2617,8 @@ var GameVehicles = (() => {
1093
2617
  });
1094
2618
  for (let i = 0; i < st.length - 1; i++)
1095
2619
  for (let k = 0; k < seg; k++) {
1096
- const a0 = i * ring + k;
1097
- const b0 = a0 + ring;
2620
+ const a0 = i * ring2 + k;
2621
+ const b0 = a0 + ring2;
1098
2622
  idx.push(a0, a0 + 1, b0, a0 + 1, b0 + 1, b0);
1099
2623
  }
1100
2624
  ;
@@ -1108,7 +2632,7 @@ var GameVehicles = (() => {
1108
2632
  col.push(cc.r, cc.g, cc.b);
1109
2633
  }
1110
2634
  for (let k = 0; k < seg; k++) {
1111
- const a = si * ring + k;
2635
+ const a = si * ring2 + k;
1112
2636
  if (e === 0) idx.push(ci, a + 1, a);
1113
2637
  else idx.push(ci, a, a + 1);
1114
2638
  }
@@ -1246,13 +2770,13 @@ var GameVehicles = (() => {
1246
2770
  });
1247
2771
  tris = next;
1248
2772
  });
1249
- function build(list) {
2773
+ function build(list2) {
1250
2774
  const g = new THREE.BufferGeometry();
1251
2775
  let off = 0;
1252
2776
  names.forEach(function(nm, a) {
1253
- const arr = new Float32Array(list.length * 3 * sizes[a]);
2777
+ const arr = new Float32Array(list2.length * 3 * sizes[a]);
1254
2778
  let w = 0;
1255
- list.forEach(function(t) {
2779
+ list2.forEach(function(t) {
1256
2780
  for (let k = 0; k < 3; k++) for (let q = 0; q < sizes[a]; q++) arr[w++] = t[k][off + q];
1257
2781
  });
1258
2782
  g.setAttribute(nm, new THREE.BufferAttribute(arr, sizes[a]));
@@ -1267,10 +2791,10 @@ var GameVehicles = (() => {
1267
2791
  });
1268
2792
  return out;
1269
2793
  }
1270
- function mergeGeometries(list) {
2794
+ function mergeGeometries(list2) {
1271
2795
  const pos = [];
1272
2796
  const nor = [];
1273
- list.forEach(function(g2) {
2797
+ list2.forEach(function(g2) {
1274
2798
  const n = g2.index ? g2.toNonIndexed() : g2;
1275
2799
  if (!n.attributes.normal) n.computeVertexNormals();
1276
2800
  const pa = n.attributes.position.array;
@@ -1329,13 +2853,13 @@ var GameVehicles = (() => {
1329
2853
  };
1330
2854
  kit.holeSide = function(poly, o) {
1331
2855
  o = o || {};
1332
- let area = 0;
2856
+ let area3 = 0;
1333
2857
  for (let i = 0; i < poly.length; i++) {
1334
2858
  const a = poly[i];
1335
2859
  const b = poly[(i + 1) % poly.length];
1336
- area += a[0] * b[1] - b[0] * a[1];
2860
+ area3 += a[0] * b[1] - b[0] * a[1];
1337
2861
  }
1338
- const sg = area > 0 ? 1 : -1;
2862
+ const sg = area3 > 0 ? 1 : -1;
1339
2863
  const E2 = poly.map(function(a, i) {
1340
2864
  const b = poly[(i + 1) % poly.length];
1341
2865
  const dx = b[0] - a[0];
@@ -1638,11 +3162,11 @@ var GameVehicles = (() => {
1638
3162
  return d.clone().applyQuaternion(this.vehicle.quaternion);
1639
3163
  }
1640
3164
  /** Declares or refines a damage zone: `c.zone(id, label, box, parts, options)`. Pass `label === false` to remove an auto-derived zone. */
1641
- zone(id, label, box, parts, o) {
3165
+ zone(id, label, box2, parts, o) {
1642
3166
  const d = Object.assign({ id }, o || {});
1643
3167
  if (label === false) d.remove = true;
1644
3168
  else if (label) d.name = label;
1645
- if (box) d.box = I.normalizeBox(box);
3169
+ if (box2) d.box = I.normalizeBox(box2);
1646
3170
  if (parts) d.objs = Array.isArray(parts) ? parts.slice() : [parts];
1647
3171
  this.zoneDefs.push(d);
1648
3172
  return d;
@@ -1658,6 +3182,7 @@ var GameVehicles = (() => {
1658
3182
  const V3 = THREE.Vector3;
1659
3183
  const PI = Math.PI;
1660
3184
  const I = kit._;
3185
+ const lod = kit.lod;
1661
3186
  const { crease } = I;
1662
3187
  function makeWarp(o) {
1663
3188
  function sec(y) {
@@ -1755,6 +3280,7 @@ var GameVehicles = (() => {
1755
3280
  });
1756
3281
  }
1757
3282
  function resample(st, n) {
3283
+ if (lod.on) n = Math.max(lod.minStations, Math.round(n * lod.stationFraction));
1758
3284
  const out = [];
1759
3285
  const L = st.length;
1760
3286
  function P(i) {
@@ -1786,11 +3312,12 @@ var GameVehicles = (() => {
1786
3312
  }
1787
3313
  function smoothLoft(st, o) {
1788
3314
  o = o || {};
3315
+ if (lod.on) o = Object.assign({}, o, { seg: Math.max(lod.minLoftSegments, Math.round((o.seg || 24) * lod.loftSegmentFraction)) });
1789
3316
  if (o.smooth) st = resample(st, o.smooth);
1790
3317
  const seg = o.seg || 24;
1791
3318
  const pos = [];
1792
3319
  const idx = [];
1793
- const ring = seg + 1;
3320
+ const ring2 = seg + 1;
1794
3321
  const axisY = o.axis === "y";
1795
3322
  st.forEach(function(s) {
1796
3323
  const a0 = s[1];
@@ -1812,8 +3339,8 @@ var GameVehicles = (() => {
1812
3339
  });
1813
3340
  for (let i = 0; i < st.length - 1; i++)
1814
3341
  for (let k = 0; k < seg; k++) {
1815
- const A = i * ring + k;
1816
- const B = A + ring;
3342
+ const A = i * ring2 + k;
3343
+ const B = A + ring2;
1817
3344
  if (axisY) idx.push(A, B, A + 1, A + 1, B, B + 1);
1818
3345
  else idx.push(A, A + 1, B, A + 1, B + 1, B);
1819
3346
  }
@@ -1826,7 +3353,7 @@ var GameVehicles = (() => {
1826
3353
  if (axisY) pos.push(cc, s[0], s[5] || 0);
1827
3354
  else pos.push(s[0], cc, s[5] || 0);
1828
3355
  for (let k = 0; k < seg; k++) {
1829
- const a = si * ring + k;
3356
+ const a = si * ring2 + k;
1830
3357
  const flip = e === 0 !== axisY;
1831
3358
  if (flip) idx.push(ci, a + 1, a);
1832
3359
  else idx.push(ci, a, a + 1);
@@ -2058,15 +3585,15 @@ var GameVehicles = (() => {
2058
3585
  c.part("glass", L, m, pos, o.reach || 0.9);
2059
3586
  return m;
2060
3587
  };
2061
- kit.asGlass = function(c, mesh, label, type, o) {
3588
+ kit.asGlass = function(c, mesh2, label, type, o) {
2062
3589
  o = o || {};
2063
- const i = c.deform.indexOf(mesh);
3590
+ const i = c.deform.indexOf(mesh2);
2064
3591
  if (i >= 0) c.deform.splice(i, 1);
2065
- if (mesh.parent) mesh.parent.remove(mesh);
2066
- const geo = mesh.geometry;
2067
- geo.applyMatrix4(new THREE.Matrix4().compose(mesh.position, mesh.quaternion, mesh.scale));
3592
+ if (mesh2.parent) mesh2.parent.remove(mesh2);
3593
+ const geo = mesh2.geometry;
3594
+ geo.applyMatrix4(new THREE.Matrix4().compose(mesh2.position, mesh2.quaternion, mesh2.scale));
2068
3595
  if (o.reuv !== false) o.reuv = true;
2069
- return kit.addGlass(c, geo, label, type, Object.assign({ mat: mesh.material }, o));
3596
+ return kit.addGlass(c, geo, label, type, Object.assign({ mat: mesh2.material }, o));
2070
3597
  };
2071
3598
  kit.sideWindows = function(c, polys, Wg, T, label, o) {
2072
3599
  o = o || {};
@@ -2112,12 +3639,12 @@ var GameVehicles = (() => {
2112
3639
  });
2113
3640
  });
2114
3641
  };
2115
- kit.cutMesh = function(mesh, tests, deg, o) {
2116
- const src = o && o.sub ? subdivide(mesh.geometry, o.sub) : mesh.geometry;
3642
+ kit.cutMesh = function(mesh2, tests, deg, o) {
3643
+ const src = o && o.sub ? subdivide(mesh2.geometry, o.sub) : mesh2.geometry;
2117
3644
  const r = cutGeometry(src, tests);
2118
3645
  crease(r.geo, deg || 42);
2119
- mesh.geometry.dispose();
2120
- mesh.geometry = r.geo;
3646
+ mesh2.geometry.dispose();
3647
+ mesh2.geometry = r.geo;
2121
3648
  return r.cut;
2122
3649
  };
2123
3650
  kit.quadDef = function(pb, pt, Wg, T, which, bev, o) {
@@ -2317,8 +3844,8 @@ var GameVehicles = (() => {
2317
3844
  const pz = nR === 1 ? 0 : (i ? 1 : -1) * w / 4;
2318
3845
  const back = gm(g, new THREE.CylinderGeometry(r * 1.06, r * 1.06, d * 0.8, 22), MATERIALS.housing, 0, 0, pz);
2319
3846
  back.rotation.z = PI / 2;
2320
- const ring = gm(g, new THREE.TorusGeometry(r * 1.02, 0.013, 8, 30), MATERIALS.chrome, d / 2 - 6e-3, 0, pz);
2321
- ring.rotation.y = PI / 2;
3847
+ const ring2 = gm(g, new THREE.TorusGeometry(r * 1.02, 0.013, 8, 30), MATERIALS.chrome, d / 2 - 6e-3, 0, pz);
3848
+ ring2.rotation.y = PI / 2;
2322
3849
  const bowl = gm(g, new THREE.SphereGeometry(r * 0.96, 18, 10, 0, PI * 2, 0, PI / 2), MATERIALS.reflectorLit, d / 2 - 0.012, 0, pz);
2323
3850
  bowl.rotation.z = PI / 2;
2324
3851
  bowl.scale.y = 0.55;
@@ -2395,8 +3922,8 @@ var GameVehicles = (() => {
2395
3922
  const e = gm(g, new THREE.CylinderGeometry(r * 0.9, r * 0.9, 0.012, 20), q * ls > 0 ? MATERIALS.tail : MATERIALS.tail, d / 2 - 0.02, 0, pz);
2396
3923
  e.rotation.z = PI / 2;
2397
3924
  inner.push(e);
2398
- const ring = gm(g, new THREE.TorusGeometry(r, 0.01, 6, 24), MATERIALS.chrome, d / 2 - 4e-3, 0, pz);
2399
- ring.rotation.y = PI / 2;
3925
+ const ring2 = gm(g, new THREE.TorusGeometry(r, 0.01, 6, 24), MATERIALS.chrome, d / 2 - 4e-3, 0, pz);
3926
+ ring2.rotation.y = PI / 2;
2400
3927
  const ln = gm(g, new THREE.SphereGeometry(r, 16, 8, 0, PI * 2, 0, PI / 2), MATERIALS.lensRed, d / 2 - 0.01, 0, pz);
2401
3928
  ln.rotation.z = -PI / 2;
2402
3929
  ln.scale.y = 0.25;
@@ -2489,7 +4016,7 @@ var GameVehicles = (() => {
2489
4016
  } else if (st === "mesh") {
2490
4017
  gm(g, roundedBoxGeometry(0.05, h, w, Math.min(0.03, h * 0.3)), MATERIALS.housing);
2491
4018
  const cell = 0.026;
2492
- const list = [];
4019
+ const list2 = [];
2493
4020
  const rows = Math.floor(h * 0.85 / (cell * 1.6));
2494
4021
  const cols = Math.floor(w * 0.9 / (cell * 1.8));
2495
4022
  for (let rI = 0; rI < rows; rI++)
@@ -2498,9 +4025,9 @@ var GameVehicles = (() => {
2498
4025
  hg.rotateZ(PI / 2);
2499
4026
  hg.rotateX(PI / 6);
2500
4027
  hg.translate(0.026, -h * 0.42 + (rI + 0.5) * h * 0.85 / rows, -w * 0.45 + (cI + 0.5 + rI % 2 * 0.5 - 0.25) * w * 0.9 / cols);
2501
- list.push(hg);
4028
+ list2.push(hg);
2502
4029
  }
2503
- if (list.length) gm(g, mergeGeometries(list), MATERIALS.trim);
4030
+ if (list2.length) gm(g, mergeGeometries(list2), MATERIALS.trim);
2504
4031
  gm(g, roundedBoxGeometry(0.022, 0.014, w, 6e-3), MATERIALS.chrome, 0.03, h / 2 - 8e-3, 0);
2505
4032
  } else if (st === "big") {
2506
4033
  const fr = roundedBoxGeometry(0.06, h, w, 0.03);
@@ -3341,7 +4868,7 @@ var GameVehicles = (() => {
3341
4868
  m.underbodyGroup = ug;
3342
4869
  const DK = makeMaterial("#1a1b1c", { roughness: 0.75, metalness: 0.3 });
3343
4870
  const ST = MATERIALS.steel;
3344
- function box(w, h, d, mat, x, y, z) {
4871
+ function box2(w, h, d, mat, x, y, z) {
3345
4872
  return c.roundBox(w, h, d, Math.min(0.02, h * 0.4), mat, x, y, z, { parent: ug });
3346
4873
  }
3347
4874
  function rod(a, b, r, mat) {
@@ -3358,9 +4885,9 @@ var GameVehicles = (() => {
3358
4885
  rod(new V3(rx, rr, -zW), new V3(rx, rr, zW), 0.045, ST);
3359
4886
  c.sphere(0.13, ST, rx, rr, 0, 1, 0.85, 1, { parent: ug });
3360
4887
  rod(new V3(rx + 0.1, rr, 0), new V3(fx - 0.6, yU + 0.04, 0), 0.035, ST);
3361
- } else box(0.09, 0.08, zW * 2, DK, rx + 0.08, Math.max(yU, rr - 0.12), 0);
4888
+ } else box2(0.09, 0.08, zW * 2, DK, rx + 0.08, Math.max(yU, rr - 0.12), 0);
3362
4889
  if (!ev) {
3363
- box(Math.min(0.5, (fx - rx) * 0.2), 0.1, 0.38, DK, fx - 0.25, yU + 0.01, -0.05);
4890
+ box2(Math.min(0.5, (fx - rx) * 0.2), 0.1, 0.38, DK, fx - 0.25, yU + 0.01, -0.05);
3364
4891
  const tz = W * 0.3;
3365
4892
  const mx = rx - 0.15;
3366
4893
  const ty = Math.max(0.14, by - 0.06);
@@ -3377,10 +4904,10 @@ var GameVehicles = (() => {
3377
4904
  { parent: ug, seg: 30, rs: 6 }
3378
4905
  );
3379
4906
  c.cylinder(0.1, 0.1, 0.42, ST, mx, ty + 0.02, tz, "x", { parent: ug });
3380
- box(0.24, 0.06, 0.2, DK, (fx + rx) / 2 + 0.1, ty, 0.16);
3381
- box(0.55, 0.18, W * 0.5, makeMaterial("#232527", { roughness: 0.7 }), rx + 0.62, Math.max(0.16, by - 0.02), -W * 0.1);
4907
+ box2(0.24, 0.06, 0.2, DK, (fx + rx) / 2 + 0.1, ty, 0.16);
4908
+ box2(0.55, 0.18, W * 0.5, makeMaterial("#232527", { roughness: 0.7 }), rx + 0.62, Math.max(0.16, by - 0.02), -W * 0.1);
3382
4909
  } else {
3383
- box(
4910
+ box2(
3384
4911
  (fx - rx) * 0.72,
3385
4912
  0.1,
3386
4913
  W - 0.4,
@@ -3389,7 +4916,7 @@ var GameVehicles = (() => {
3389
4916
  Math.max(0.14, by - 0.03),
3390
4917
  0
3391
4918
  );
3392
- for (let k = 0; k < 6; k++) box(0.03, 0.02, W - 0.45, ST, rx + 0.4 + k * (fx - rx - 0.8) / 5, Math.max(0.14, by - 0.03) - 0.06, 0);
4919
+ for (let k = 0; k < 6; k++) box2(0.03, 0.02, W - 0.45, ST, rx + 0.4 + k * (fx - rx - 0.8) / 5, Math.max(0.14, by - 0.03) - 0.06, 0);
3393
4920
  }
3394
4921
  if (o.spare && m.rear < rx - 0.7) {
3395
4922
  c.cylinder(0.32, 0.32, 0.2, MATERIALS.rubber, rx - 0.55, Math.max(0.2, by - 0.05), 0, null, { parent: ug });
@@ -3717,16 +5244,16 @@ var GameVehicles = (() => {
3717
5244
  }
3718
5245
  } else {
3719
5246
  const ns = o.spokes || 28;
3720
- const list = [];
5247
+ const list2 = [];
3721
5248
  for (let jj = 0; jj < ns; jj++) {
3722
5249
  const a2 = jj / ns * PI * 2;
3723
5250
  const cg = new THREE.CylinderGeometry(25e-4, 25e-4, sl, 4);
3724
5251
  cg.applyMatrix4(
3725
5252
  new THREE.Matrix4().makeRotationZ(a2 - PI / 2).setPosition(Math.cos(a2) * sl / 2, Math.sin(a2) * sl / 2, (jj % 2 ? 1 : -1) * 0.012)
3726
5253
  );
3727
- list.push(cg);
5254
+ list2.push(cg);
3728
5255
  }
3729
- g.add(new THREE.Mesh(mergeGeometries(list), MATERIALS.chrome));
5256
+ g.add(new THREE.Mesh(mergeGeometries(list2), MATERIALS.chrome));
3730
5257
  }
3731
5258
  const hub = new THREE.Mesh(new THREE.CylinderGeometry(0.035, 0.035, 0.1, 12), MATERIALS.steel);
3732
5259
  hub.rotation.x = PI / 2;
@@ -3908,9 +5435,9 @@ var GameVehicles = (() => {
3908
5435
  return puffTexture(k * 17, true);
3909
5436
  });
3910
5437
  }
3911
- const list = fire ? FIRE_TEXTURES : SMOKE_TEXTURES;
5438
+ const list2 = fire ? FIRE_TEXTURES : SMOKE_TEXTURES;
3912
5439
  const mat = new THREE.SpriteMaterial({
3913
- map: list[Math.floor(Math.random() * list.length)],
5440
+ map: list2[Math.floor(Math.random() * list2.length)],
3914
5441
  color: new THREE.Color(col),
3915
5442
  transparent: true,
3916
5443
  depthWrite: false,
@@ -5219,7 +6746,7 @@ var GameVehicles = (() => {
5219
6746
  }
5220
6747
  });
5221
6748
  const dset = new Set(c.deform);
5222
- const box = new THREE.Box3();
6749
+ const box2 = new THREE.Box3();
5223
6750
  c.vehicle.children.forEach(function(o) {
5224
6751
  if (dset.has(o) || o.userData.noFollow) return;
5225
6752
  if (!(o.isMesh || o.isGroup && o.userData.rigid)) return;
@@ -5230,10 +6757,10 @@ var GameVehicles = (() => {
5230
6757
  if (!o.geometry.boundingBox) o.geometry.computeBoundingBox();
5231
6758
  o.userData.bb0 = o.geometry.boundingBox.clone().applyMatrix4(o.matrix);
5232
6759
  } else {
5233
- box.setFromObject(o);
5234
- box.applyMatrix4(gInv);
5235
- o.userData.c0 = box.getCenter(new V3());
5236
- o.userData.bb0 = box.clone();
6760
+ box2.setFromObject(o);
6761
+ box2.applyMatrix4(gInv);
6762
+ o.userData.c0 = box2.getCenter(new V3());
6763
+ o.userData.bb0 = box2.clone();
5237
6764
  }
5238
6765
  o.userData.pos0 = o.position.clone();
5239
6766
  }
@@ -5529,11 +7056,11 @@ var GameVehicles = (() => {
5529
7056
  [12, "#d8322a", kit.t("blast.lethal", { m: 12 })],
5530
7057
  [30, "#e0a548", kit.t("blast.injury", { m: 30 })]
5531
7058
  ].forEach(function(r) {
5532
- const ring = new THREE.Mesh(new THREE.RingGeometry(r[0] - 0.25, r[0], 96), boomMat(r[1], 0.85));
5533
- ring.rotation.x = -PI / 2;
5534
- ring.position.y = 0.06;
5535
- ring.renderOrder = 5;
5536
- g.add(ring);
7059
+ const ring2 = new THREE.Mesh(new THREE.RingGeometry(r[0] - 0.25, r[0], 96), boomMat(r[1], 0.85));
7060
+ ring2.rotation.x = -PI / 2;
7061
+ ring2.position.y = 0.06;
7062
+ ring2.renderOrder = 5;
7063
+ g.add(ring2);
5537
7064
  const fill = new THREE.Mesh(new THREE.CircleGeometry(r[0], 96), boomMat(r[1], 0.07));
5538
7065
  fill.rotation.x = -PI / 2;
5539
7066
  fill.position.y = 0.05;
@@ -5550,7 +7077,7 @@ var GameVehicles = (() => {
5550
7077
  lb.rotation.x = -PI / 2;
5551
7078
  lb.position.set(0, 0.08, r[0] + r[0] * 0.06);
5552
7079
  g.add(lb);
5553
- c.bigFx.rings.push(ring, fill, lb);
7080
+ c.bigFx.rings.push(ring2, fill, lb);
5554
7081
  });
5555
7082
  }
5556
7083
  function detonate(c) {
@@ -5825,7 +7352,7 @@ var GameVehicles = (() => {
5825
7352
  const I = kit._;
5826
7353
  const { MATERIALS } = I;
5827
7354
  function rollMeshes(c) {
5828
- const list = [];
7355
+ const list2 = [];
5829
7356
  const thin = [];
5830
7357
  c.root.updateMatrixWorld(true);
5831
7358
  const inv = new THREE.Matrix4().copy(c.vehicle.matrixWorld).invert();
@@ -5843,9 +7370,9 @@ var GameVehicles = (() => {
5843
7370
  thin.push({ o: ob, M });
5844
7371
  return;
5845
7372
  }
5846
- list.push({ o: ob, M });
7373
+ list2.push({ o: ob, M });
5847
7374
  });
5848
- return { list, thin };
7375
+ return { list: list2, thin };
5849
7376
  }
5850
7377
  function convexHull(pts) {
5851
7378
  pts.sort(function(a, b) {
@@ -5928,7 +7455,7 @@ var GameVehicles = (() => {
5928
7455
  };
5929
7456
  function rollContact(c, A, q) {
5930
7457
  const m = c.meta;
5931
- const list = [];
7458
+ const list2 = [];
5932
7459
  const dy = A.cen[0] - q[0];
5933
7460
  const dz = A.cen[1] - q[1];
5934
7461
  const dl = Math.hypot(dy, dz) || 1;
@@ -5936,17 +7463,17 @@ var GameVehicles = (() => {
5936
7463
  const f = 0.75;
5937
7464
  const bike = m.kind === "bike";
5938
7465
  (bike ? [0] : [-0.28, 0, 0.28]).forEach(function(k) {
5939
- list.push([new V3(A.x + k * A.L, q[0], q[1]), dir]);
7466
+ list2.push([new V3(A.x + k * A.L, q[0], q[1]), dir]);
5940
7467
  });
5941
7468
  if (!bike && Math.abs(dir.z) > 0.75 && m.top - m.belt > 0.25 && A.hits < 3) {
5942
7469
  const sd = -Math.sign(dir.z);
5943
7470
  const yw = m.belt + (m.top - m.belt) * 0.45;
5944
7471
  [-0.25, 0.2].forEach(function(k) {
5945
7472
  const h = I.probe(c, new V3(A.x + k * A.L, yw, sd * (m.width / 2 + 2)), new V3(0, 0, -sd), 2 + m.width);
5946
- if (h) list.push([h.p, new V3(0, 0, -sd)]);
7473
+ if (h) list2.push([h.p, new V3(0, 0, -sd)]);
5947
7474
  });
5948
7475
  }
5949
- list.forEach(function(e) {
7476
+ list2.forEach(function(e) {
5950
7477
  I.impact(c, e[0], e[1], f, { noStain: true, maxDepth: 0.1 });
5951
7478
  const pf = c.toEffects(e[0]);
5952
7479
  pf.y = Math.max(0.03, pf.y);
@@ -5956,15 +7483,15 @@ var GameVehicles = (() => {
5956
7483
  });
5957
7484
  A.hits++;
5958
7485
  A.bounce = A.hits === 1 ? 0.06 : 0.1;
5959
- if (m.kind === "boat") I.burst(c, "water", c.toEffects(list[0][0]).setY(0.05), 30, { speed: 3 });
7486
+ if (m.kind === "boat") I.burst(c, "water", c.toEffects(list2[0][0]).setY(0.05), 30, { speed: 3 });
5960
7487
  }
5961
- function exactMinY(c, list) {
7488
+ function exactMinY(c, list2) {
5962
7489
  c.root.updateMatrixWorld(true);
5963
7490
  const inv = new THREE.Matrix4().copy(c.root.matrixWorld).invert();
5964
7491
  const M = new THREE.Matrix4();
5965
7492
  const v = new V3();
5966
7493
  let mn = 1e9;
5967
- list.forEach(function(e) {
7494
+ list2.forEach(function(e) {
5968
7495
  const ob = e.o;
5969
7496
  if (!ob.parent || !I.shown(c, ob)) return;
5970
7497
  M.multiplyMatrices(inv, ob.matrixWorld);
@@ -8160,9 +9687,9 @@ var GameVehicles = (() => {
8160
9687
  down: new V3(0, 1, 0)
8161
9688
  }[d] || null;
8162
9689
  }
8163
- function resolveObjs(c, list, z) {
9690
+ function resolveObjs(c, list2, z) {
8164
9691
  const out = [];
8165
- (list || []).forEach(function(o) {
9692
+ (list2 || []).forEach(function(o) {
8166
9693
  if (!o) return;
8167
9694
  if (typeof o === "string") {
8168
9695
  c.parts.forEach(function(p) {
@@ -8190,22 +9717,22 @@ var GameVehicles = (() => {
8190
9717
  }
8191
9718
  function deriveZones(c) {
8192
9719
  const m = c.meta;
8193
- let list = [];
9720
+ let list2 = [];
8194
9721
  const byId = {};
8195
9722
  (m.zones === false ? [] : I.autoZones(c)).forEach(function(z) {
8196
9723
  byId[z.id] = z;
8197
- list.push(z);
9724
+ list2.push(z);
8198
9725
  });
8199
9726
  (c.zoneDefs || []).forEach(function(d) {
8200
9727
  let z = byId[d.id];
8201
9728
  if (d.remove) {
8202
9729
  if (z) {
8203
- list.splice(list.indexOf(z), 1);
9730
+ list2.splice(list2.indexOf(z), 1);
8204
9731
  delete byId[d.id];
8205
9732
  }
8206
- list.slice().forEach(function(q) {
9733
+ list2.slice().forEach(function(q) {
8207
9734
  if (q.id.indexOf(d.id + "_") === 0 && /^\d+$/.test(q.id.slice(d.id.length + 1))) {
8208
- list.splice(list.indexOf(q), 1);
9735
+ list2.splice(list2.indexOf(q), 1);
8209
9736
  delete byId[q.id];
8210
9737
  }
8211
9738
  });
@@ -8218,7 +9745,7 @@ var GameVehicles = (() => {
8218
9745
  } else {
8219
9746
  z = Object.assign({ type: "panel" }, d);
8220
9747
  byId[d.id] = z;
8221
- list.push(z);
9748
+ list2.push(z);
8222
9749
  }
8223
9750
  });
8224
9751
  const inv = I.worldInverseOf(c);
@@ -8251,10 +9778,10 @@ var GameVehicles = (() => {
8251
9778
  }
8252
9779
  z.box = I.normalizeBox(bb);
8253
9780
  });
8254
- list = list.filter(function(z) {
9781
+ list2 = list2.filter(function(z) {
8255
9782
  return z.box && z.name;
8256
9783
  });
8257
- list.forEach(function(z) {
9784
+ list2.forEach(function(z) {
8258
9785
  z.glass = (z.glass || []).slice();
8259
9786
  z.lights = (z.lights || []).slice();
8260
9787
  z.wheels = (z.wheels || []).slice();
@@ -8291,7 +9818,7 @@ var GameVehicles = (() => {
8291
9818
  if (owner.has(g) || !g.parent || !g.userData.glass || g.userData.ghost) return;
8292
9819
  const cg = I.localBox(c, g, inv).getCenter(new V3());
8293
9820
  let best = null;
8294
- list.forEach(function(z) {
9821
+ list2.forEach(function(z) {
8295
9822
  if (z.internal || z.type === "wheel" || z.type === "light") return;
8296
9823
  if (I.boxContains(z.box, cg.x, cg.y, cg.z, 0.05) && (!best || I.boxVolume(z.box) < I.boxVolume(best.box))) best = z;
8297
9824
  });
@@ -8300,7 +9827,7 @@ var GameVehicles = (() => {
8300
9827
  owner.set(g, best);
8301
9828
  }
8302
9829
  });
8303
- const looseParts = list.filter(function(z) {
9830
+ const looseParts = list2.filter(function(z) {
8304
9831
  return z.type === "part";
8305
9832
  });
8306
9833
  if (looseParts.length)
@@ -8325,7 +9852,7 @@ var GameVehicles = (() => {
8325
9852
  });
8326
9853
  }
8327
9854
  });
8328
- list.forEach(function(z) {
9855
+ list2.forEach(function(z) {
8329
9856
  if (!z.mirror) return;
8330
9857
  z.mirrors = [];
8331
9858
  const B = z.box;
@@ -8357,21 +9884,21 @@ var GameVehicles = (() => {
8357
9884
  });
8358
9885
  }
8359
9886
  });
8360
- list.forEach(function(z) {
9887
+ list2.forEach(function(z) {
8361
9888
  const B = z.box;
8362
9889
  z.map = z.map || [Math.max(B.x0, R - 0.3), Math.min(B.x1, F + 0.3), Math.max(B.z0, -W / 2 - 0.35), Math.min(B.z1, W / 2 + 0.35)];
8363
9890
  });
8364
- carMap(c, list);
9891
+ carMap(c, list2);
8365
9892
  c.zoneOwners = owner;
8366
- return list;
9893
+ return list2;
8367
9894
  }
8368
- function carMap(c, list) {
9895
+ function carMap(c, list2) {
8369
9896
  const m = c.meta;
8370
9897
  const W = m.width || 1.7;
8371
9898
  const hw = W / 2;
8372
9899
  const gh = m.glasshouse;
8373
9900
  if ((m.kind || "car") !== "car" || !gh || gh.fb == null) return;
8374
- list.forEach(function(z) {
9901
+ list2.forEach(function(z) {
8375
9902
  if (z.mapSet) return;
8376
9903
  const B = z.box;
8377
9904
  const s = z.side;
@@ -8436,8 +9963,8 @@ var GameVehicles = (() => {
8436
9963
  const top = m.top || belt + 0.5;
8437
9964
  const hw = W / 2;
8438
9965
  const WH = I.wheelInfo(c);
8439
- function add(id, name, type, box, o) {
8440
- const z = Object.assign({ id, name, type, box: I.normalizeBox(box) }, o || {});
9966
+ function add(id, name, type, box2, o) {
9967
+ const z = Object.assign({ id, name, type, box: I.normalizeBox(box2) }, o || {});
8441
9968
  Z.push(z);
8442
9969
  return z;
8443
9970
  }
@@ -9800,16 +11327,167 @@ var GameVehicles = (() => {
9800
11327
  kit.ui = { damageMap, bandButtons, legend, healthBar, scenarioButtons, zoneLabel, bandRange, css, injectStyles };
9801
11328
  }
9802
11329
 
11330
+ // src/lod.ts
11331
+ var defaultLodSettings = {
11332
+ on: false,
11333
+ segmentFraction: 0.5,
11334
+ roundingRings: 1,
11335
+ subdivisionFactor: 2.2,
11336
+ flatBoxEdge: 0.22,
11337
+ minLoftSegments: 14,
11338
+ loftSegmentFraction: 0.45,
11339
+ minStations: 20,
11340
+ stationFraction: 0.55,
11341
+ gridFactor: 2.6,
11342
+ hiddenMaterials: ["seat", "carpet", "headliner", "gauge", "screen", "bay", "hoodLiner", "block", "cover", "radiator", "coolant", "coolantLiq", "washer", "hv", "hose", "leather"]
11343
+ };
11344
+ function createLod(options) {
11345
+ return { ...defaultLodSettings, ...options || {}, on: false };
11346
+ }
11347
+ var fewer = (lod, n, min) => Math.max(min, Math.round(n * lod.segmentFraction));
11348
+ function wrapThreeForLod(TH, lod) {
11349
+ const T = Object.assign({}, TH);
11350
+ if (TH.SRGBColorSpace) {
11351
+ T.CanvasTexture = class extends TH.CanvasTexture {
11352
+ constructor(a, b, c, d, e, f, g, h, i) {
11353
+ super(a, b, c, d, e, f, g, h, i);
11354
+ this.colorSpace = TH.SRGBColorSpace;
11355
+ }
11356
+ };
11357
+ }
11358
+ T.CylinderGeometry = class extends TH.CylinderGeometry {
11359
+ constructor(rt, rb, h, rs, hs, open, ts, tl) {
11360
+ super(rt, rb, h, lod.on && rs != null ? fewer(lod, rs, 6) : rs, lod.on ? 1 : hs, open, ts, tl);
11361
+ }
11362
+ };
11363
+ T.SphereGeometry = class extends TH.SphereGeometry {
11364
+ constructor(r, ws, hs, ps, pl, ts, tl) {
11365
+ super(r, lod.on && ws != null ? fewer(lod, ws, 8) : ws, lod.on && hs != null ? fewer(lod, hs, 5) : hs, ps, pl, ts, tl);
11366
+ }
11367
+ };
11368
+ T.TorusGeometry = class extends TH.TorusGeometry {
11369
+ constructor(r, tube, rs, ts, arc) {
11370
+ super(r, tube, lod.on && rs != null ? fewer(lod, rs, 4) : rs, lod.on && ts != null ? fewer(lod, ts, 8) : ts, arc);
11371
+ }
11372
+ };
11373
+ T.LatheGeometry = class extends TH.LatheGeometry {
11374
+ constructor(pts, seg, a, b) {
11375
+ super(pts, lod.on && seg != null ? fewer(lod, seg, 10) : seg, a, b);
11376
+ }
11377
+ };
11378
+ T.TubeGeometry = class extends TH.TubeGeometry {
11379
+ constructor(path, ts, r, rs, closed) {
11380
+ if (lod.on) {
11381
+ let len = 1;
11382
+ try {
11383
+ len = path.getLength();
11384
+ } catch (e) {
11385
+ }
11386
+ ts = Math.max(6, Math.min(ts == null ? 64 : ts, Math.ceil(len / 0.14)));
11387
+ rs = r < 0.025 ? 3 : Math.max(4, fewer(lod, rs == null ? 8 : rs, 4));
11388
+ }
11389
+ super(path, ts, r, rs, closed);
11390
+ }
11391
+ };
11392
+ T.ExtrudeGeometry = class extends TH.ExtrudeGeometry {
11393
+ constructor(shape, o) {
11394
+ if (lod.on && o) {
11395
+ o = Object.assign({}, o);
11396
+ if (o.curveSegments) o.curveSegments = fewer(lod, o.curveSegments, 5);
11397
+ if (o.bevelSegments) o.bevelSegments = Math.max(1, Math.round(o.bevelSegments * 0.5));
11398
+ }
11399
+ super(shape, o);
11400
+ }
11401
+ };
11402
+ T.CircleGeometry = class extends TH.CircleGeometry {
11403
+ constructor(r, seg, a, b) {
11404
+ super(r, lod.on && seg != null ? fewer(lod, seg, 8) : seg, a, b);
11405
+ }
11406
+ };
11407
+ T.RingGeometry = class extends TH.RingGeometry {
11408
+ constructor(a, b, ts, ps, c, d) {
11409
+ super(a, b, lod.on && ts != null ? fewer(lod, ts, 8) : ts, ps, c, d);
11410
+ }
11411
+ };
11412
+ return T;
11413
+ }
11414
+ var INTERIOR_FUNCTIONS = ["interior", "engineBay", "seat", "dashboard", "steeringWheel", "yoke", "busRows"];
11415
+ function installLod(kit) {
11416
+ const lod = kit.lod;
11417
+ const meshesOf = (c) => {
11418
+ const set = /* @__PURE__ */ new Set();
11419
+ c.vehicle.traverse((o) => {
11420
+ if (o.isMesh) set.add(o);
11421
+ });
11422
+ return set;
11423
+ };
11424
+ for (const name of INTERIOR_FUNCTIONS) {
11425
+ const original = kit[name];
11426
+ if (typeof original !== "function") continue;
11427
+ kit[name] = function(...args) {
11428
+ const c = args[0];
11429
+ if (!lod.on || !c || !c.vehicle) return original.apply(this, args);
11430
+ const before = meshesOf(c);
11431
+ const result = original.apply(this, args);
11432
+ c.vehicle.traverse((o) => {
11433
+ if (o.isMesh && !before.has(o)) o.userData.skipLite = true;
11434
+ });
11435
+ return result;
11436
+ };
11437
+ }
11438
+ kit.lodHiddenMaterials = function() {
11439
+ const materials = kit.materials;
11440
+ return new Set(lod.hiddenMaterials.map((name) => materials[name]).filter(Boolean));
11441
+ };
11442
+ }
11443
+
11444
+ // src/damageables.ts
11445
+ var defaultDamageableKinds = {
11446
+ door: { material: "metal", part: "door" },
11447
+ hood: { material: "metal", part: "hood" },
11448
+ lid: { material: "metal", part: "lid" },
11449
+ bumper: { material: "metal", part: "bumper" },
11450
+ glass: { material: "glass", part: "window" },
11451
+ "glass:windshield": { material: "glass", part: "windshield" },
11452
+ light: { material: "glass", part: "headlight" },
11453
+ "light:taillight": { material: "glass", part: "taillight" },
11454
+ wheel: { material: "rubber", part: "wheel" },
11455
+ "internal:fuel_tank": { material: "fuel", part: "fuel_tank" }
11456
+ };
11457
+ function installDamageables(kit) {
11458
+ const THREE = kit.THREE;
11459
+ kit.damageableKinds = { ...defaultDamageableKinds };
11460
+ const kindOf2 = (zone) => {
11461
+ const kinds = kit.damageableKinds;
11462
+ if (zone.type === "internal") return zone.sub ? kinds[`internal:${zone.sub}`] || null : null;
11463
+ return kinds[`${zone.type}:${zone.fxKey}`] || kinds[zone.type] || null;
11464
+ };
11465
+ kit.damageables = function(c, matrix) {
11466
+ const out = [];
11467
+ for (const zone of c.zones || []) {
11468
+ const kind = kindOf2(zone);
11469
+ if (!kind || !zone.box) continue;
11470
+ const b = zone.box;
11471
+ const box2 = new THREE.Box3(new THREE.Vector3(b.x0, b.y0, b.z0), new THREE.Vector3(b.x1, b.y1, b.z1));
11472
+ if (matrix) box2.applyMatrix4(matrix);
11473
+ out.push({ material: kind.material, zone: zone.id, part: kind.part, side: zone.side === 0 ? "center" : zone.side < 0 ? "left" : "right", box: box2 });
11474
+ }
11475
+ return out;
11476
+ };
11477
+ }
11478
+
9803
11479
  // src/kit.ts
9804
11480
  function createVehicleKit(options) {
9805
11481
  const opts = options || {};
9806
- const THREE = opts.THREE;
9807
- if (!THREE || !THREE.Vector3) throw new Error("createVehicleKit: options.THREE (the three.js namespace) is required");
11482
+ if (!opts.THREE || !opts.THREE.Vector3) throw new Error("createVehicleKit: options.THREE (the three.js namespace) is required");
11483
+ const lod = createLod(opts.lod);
11484
+ const THREE = wrapThreeForLod(shapeGeometries(opts.THREE), lod);
9808
11485
  const strings = resolveStrings(opts.locale);
9809
11486
  const t = createTranslate(strings);
9810
11487
  const doc = opts.document || (typeof document !== "undefined" ? document : void 0);
9811
11488
  const kit = {
9812
11489
  THREE,
11490
+ lod,
9813
11491
  V3: THREE.Vector3,
9814
11492
  PI: Math.PI,
9815
11493
  document: doc,
@@ -9881,6 +11559,8 @@ var GameVehicles = (() => {
9881
11559
  installZoneApply(kit);
9882
11560
  installScenarios(kit);
9883
11561
  installUi(kit);
11562
+ installLod(kit);
11563
+ installDamageables(kit);
9884
11564
  const I = kit._;
9885
11565
  const HOOKS = {};
9886
11566
  kit.on = function(ev, fn) {
@@ -9975,9 +11655,18 @@ var GameVehicles = (() => {
9975
11655
  }
9976
11656
  }
9977
11657
  kit.defaultDamage = defaultDamage;
9978
- kit.build = function(key, state, zoneState) {
11658
+ kit.build = function(key, state, zoneState, options2) {
9979
11659
  const spec = kit.find(key);
9980
11660
  if (!spec) throw new Error("Unknown vehicle: " + key);
11661
+ const lodBefore = lod.on;
11662
+ if (options2 && options2.lod != null) lod.on = !!options2.lod;
11663
+ try {
11664
+ return buildVehicle(spec, key, state, zoneState);
11665
+ } finally {
11666
+ lod.on = lodBefore;
11667
+ }
11668
+ };
11669
+ function buildVehicle(spec, key, state, zoneState) {
9981
11670
  const st = state || "intact";
9982
11671
  const seedKey = spec.seedKey || key;
9983
11672
  const c = new kit.BuildContext(st, strHash(seedKey + (LEGACY_STATES[st] || st)));
@@ -10026,7 +11715,7 @@ var GameVehicles = (() => {
10026
11715
  if (o.isMesh && !(o.material && o.material.transparent)) o.receiveShadow = true;
10027
11716
  });
10028
11717
  return c;
10029
- };
11718
+ }
10030
11719
  kit.dispose = function(c) {
10031
11720
  const shared = Object.values(I.PARTICLE_GEOMETRIES);
10032
11721
  c.root.traverse(function(o) {
@@ -10043,8 +11732,9 @@ var GameVehicles = (() => {
10043
11732
  assignDeep(strings, resolveStrings(opts.locale));
10044
11733
  return kit;
10045
11734
  };
10046
- const { THREE: _three, locale: _locale, document: _document, plugins, ...config } = opts;
11735
+ const { THREE: _three, lod: _lod, locale: _locale, document: _document, plugins, ...config } = opts;
10047
11736
  void _three;
11737
+ void _lod;
10048
11738
  void _locale;
10049
11739
  void _document;
10050
11740
  kit.configure(config);
@@ -10052,6 +11742,51 @@ var GameVehicles = (() => {
10052
11742
  return kit;
10053
11743
  }
10054
11744
 
11745
+ // ../game-contracts/dist/index.js
11746
+ var DEFAULT_IGNITION_RULES = {
11747
+ fillCurve: [
11748
+ [0, 0.6],
11749
+ [0.1, 1],
11750
+ [0.35, 0.6],
11751
+ [0.65, 0.3],
11752
+ [0.9, 0.15],
11753
+ [1, 0.12]
11754
+ ],
11755
+ flameFillInfluence: 0.5,
11756
+ flameDefaults: { incendiary: 0.7, tracer: 0.2 },
11757
+ sparkTarget: { steel: 1, iron: 1, aluminium: 0.25 },
11758
+ sparkMetalDefault: 0.5,
11759
+ sparkFluid: { diesel: 0.15 },
11760
+ fireNear: { radius: 1.5, chancePerSecond: 0.5 }
11761
+ };
11762
+ var resolveIgnitionRules = (rules) => ({
11763
+ ...DEFAULT_IGNITION_RULES,
11764
+ ...rules,
11765
+ flameDefaults: { ...DEFAULT_IGNITION_RULES.flameDefaults, ...rules?.flameDefaults },
11766
+ sparkTarget: { ...DEFAULT_IGNITION_RULES.sparkTarget, ...rules?.sparkTarget },
11767
+ sparkFluid: { ...DEFAULT_IGNITION_RULES.sparkFluid, ...rules?.sparkFluid },
11768
+ fireNear: { ...DEFAULT_IGNITION_RULES.fireNear, ...rules?.fireNear }
11769
+ });
11770
+ var fillFactor = (fill, curve = DEFAULT_IGNITION_RULES.fillCurve) => {
11771
+ if (curve.length === 0) return 1;
11772
+ const f = Math.min(1, Math.max(0, fill));
11773
+ if (f <= curve[0][0]) return curve[0][1];
11774
+ for (let i = 1; i < curve.length; i++) {
11775
+ const [x1, y1] = curve[i];
11776
+ const [x0, y0] = curve[i - 1];
11777
+ if (f <= x1) return x1 === x0 ? y1 : y0 + (y1 - y0) * (f - x0) / (x1 - x0);
11778
+ }
11779
+ return curve[curve.length - 1][1];
11780
+ };
11781
+ var sparkTargetFactor = (materialId, metal, rules = DEFAULT_IGNITION_RULES) => !metal ? 0 : rules.sparkTarget[materialId] ?? rules.sparkMetalDefault;
11782
+ var ignitionChance = (hit, target, rules = DEFAULT_IGNITION_RULES) => {
11783
+ const k = fillFactor(target.fill, rules.fillCurve);
11784
+ const spark = (hit.ignition?.spark ?? 0) * target.sparkTarget * (target.fluid === void 0 ? 1 : rules.sparkFluid[target.fluid] ?? 1) * k;
11785
+ const flameBase = hit.ignition?.flame ?? Math.max(hit.incendiary ? rules.flameDefaults.incendiary : 0, hit.tracer ? rules.flameDefaults.tracer : 0);
11786
+ const flame = flameBase * (1 - rules.flameFillInfluence * (1 - k));
11787
+ return 1 - (1 - Math.min(1, Math.max(0, spark))) * (1 - Math.min(1, Math.max(0, flame)));
11788
+ };
11789
+
10055
11790
  // src/destructible/model.ts
10056
11791
  var JOINT_RULES = {
10057
11792
  hinge: { strength: 5e3, tools: ["wrench", "screwdriver"], seconds: 3 },
@@ -10179,19 +11914,19 @@ var GameVehicles = (() => {
10179
11914
  for (const z of zones) {
10180
11915
  let role = roleOfZone(z);
10181
11916
  if (z === hubZone) role = "frame";
10182
- const box = boxOf(z);
10183
- const area = areaOf(box.size);
11917
+ const box2 = boxOf(z);
11918
+ const area3 = areaOf(box2.size);
10184
11919
  const id = z.id;
10185
11920
  const label = String(z.name ?? id);
10186
11921
  const tags = [...z.tags ?? []];
10187
11922
  if (role === "rim") {
10188
11923
  const wheels = z.wheels ?? [];
10189
11924
  const r = Math.max(0.2, ...wheels.map((w) => w.userData?.r ?? 0.3));
10190
- const sz = Math.sign(box.center[2]) || 1;
10191
- const rimBox = { center: [box.center[0], box.center[1], box.center[2] - sz * box.size[2] * 0.25], size: [box.size[0] * 0.7, box.size[1] * 0.7, box.size[2] * 0.5] };
11925
+ const sz = Math.sign(box2.center[2]) || 1;
11926
+ const rimBox = { center: [box2.center[0], box2.center[1], box2.center[2] - sz * box2.size[2] * 0.25], size: [box2.size[0] * 0.7, box2.size[1] * 0.7, box2.size[2] * 0.5] };
10192
11927
  add({ id, role, name: label, material: kind === "plane" || kind === "heli" ? "aluminium" : "steel", box: rimBox, mass: 22 * r * Math.max(1, wheels.length), hp: 12e3 * armour, zoneId: id, visual: kind !== "plane" && !/landing/.test(id), tags: ["wheel", ...tags] });
10193
11928
  const tyreId = id.replace(/^wheels?/, "tyre").replace(/^(?!tyre)/, "tyre_");
10194
- const tyreBox = { center: box.center, size: [box.size[0] * 0.98, box.size[1] * 0.98, box.size[2]] };
11929
+ const tyreBox = { center: box2.center, size: [box2.size[0] * 0.98, box2.size[1] * 0.98, box2.size[2]] };
10195
11930
  add({
10196
11931
  id: tyreId,
10197
11932
  role: "tyre",
@@ -10211,29 +11946,29 @@ var GameVehicles = (() => {
10211
11946
  if (role === "glass") {
10212
11947
  const glass = (z.glass ?? [])[0];
10213
11948
  const laminated = glass?.userData?.glass?.type === "laminated" || m.bullet;
10214
- add({ id, role, name: label, material: "glass", box, mass: 10 * area, hp: laminated ? 4e3 : 450, zoneId: id, visual: false, tags: ["glass", ...tags] });
11949
+ add({ id, role, name: label, material: "glass", box: box2, mass: 10 * area3, hp: laminated ? 4e3 : 450, zoneId: id, visual: false, tags: ["glass", ...tags] });
10215
11950
  continue;
10216
11951
  }
10217
11952
  if (role === "light") {
10218
- add({ id, role, name: label, material: "glass", box, mass: 1.2 + 4 * area, hp: 320, zoneId: id, visual: true, tags: ["light", ...tags] });
11953
+ add({ id, role, name: label, material: "glass", box: box2, mass: 1.2 + 4 * area3, hp: 320, zoneId: id, visual: true, tags: ["light", ...tags] });
10219
11954
  continue;
10220
11955
  }
10221
11956
  if (role === "bumper") {
10222
- add({ id, role, name: label, material: "plastic", box, mass: 6 + 5 * area, hp: 9e3, ignitionC: 400, zoneId: id, visual: true, tags: ["bumper", ...tags] });
11957
+ add({ id, role, name: label, material: "plastic", box: box2, mass: 6 + 5 * area3, hp: 9e3, ignitionC: 400, zoneId: id, visual: true, tags: ["bumper", ...tags] });
10223
11958
  continue;
10224
11959
  }
10225
11960
  if (role === "engine") {
10226
11961
  const kg2 = { bike: 55, quad: 90, car: 150, tractor: 450, truck: 600, bus: 450, tank: 900, boat: 90, plane: 160, heli: 150 }[kind];
10227
- add({ id, role, name: label, material: kind === "plane" || kind === "heli" ? "aluminium" : "steel", box, mass: kg2, hp: 6e4 * armour, ignitionC: 260, zoneId: id, visual: false, tags: ["engine", ...tags] });
11962
+ add({ id, role, name: label, material: kind === "plane" || kind === "heli" ? "aluminium" : "steel", box: box2, mass: kg2, hp: 6e4 * armour, ignitionC: 260, zoneId: id, visual: false, tags: ["engine", ...tags] });
10228
11963
  continue;
10229
11964
  }
10230
11965
  if (role === "radiator") {
10231
- add({ id, role, name: label, material: "aluminium", box, mass: 8, hp: 3e3, zoneId: id, visual: false, contains: { fluid: "coolant", liters: kind === "truck" || kind === "bus" ? 18 : 7, pressurized: true }, tags: ["radiator", ...tags] });
11966
+ add({ id, role, name: label, material: "aluminium", box: box2, mass: 8, hp: 3e3, zoneId: id, visual: false, contains: { fluid: "coolant", liters: kind === "truck" || kind === "bus" ? 18 : 7, pressurized: true }, tags: ["radiator", ...tags] });
10232
11967
  continue;
10233
11968
  }
10234
11969
  if (role === "fuel_tank") {
10235
11970
  if (fuelless) {
10236
- add({ id, role: "other", name: label, material: "steel", box, mass: 20, hp: 25e3, zoneId: id, visual: false, tags });
11971
+ add({ id, role: "other", name: label, material: "steel", box: box2, mass: 20, hp: 25e3, zoneId: id, visual: false, tags });
10237
11972
  continue;
10238
11973
  }
10239
11974
  const liters = { bike: 14, quad: 16, car: 55, tractor: 150, truck: 300, bus: 200, tank: 600, boat: 90, plane: 120, heli: 300 }[kind];
@@ -10243,7 +11978,7 @@ var GameVehicles = (() => {
10243
11978
  role,
10244
11979
  name: label,
10245
11980
  material: "steel",
10246
- box,
11981
+ box: box2,
10247
11982
  mass: 14 + liters * 0.8,
10248
11983
  hp: 25e3 * armour,
10249
11984
  zoneId: id,
@@ -10256,21 +11991,21 @@ var GameVehicles = (() => {
10256
11991
  continue;
10257
11992
  }
10258
11993
  if (role === "battery") {
10259
- add({ id, role, name: label, material: "plastic", box, mass: 14, hp: 1500, zoneId: id, visual: true, ignitionC: 200, tags: ["battery", ...tags] });
11994
+ add({ id, role, name: label, material: "plastic", box: box2, mass: 14, hp: 1500, zoneId: id, visual: true, ignitionC: 200, tags: ["battery", ...tags] });
10260
11995
  continue;
10261
11996
  }
10262
11997
  if (role === "frame") {
10263
- add({ id, role, name: label, material: "steel", box: floorPan(box), mass: mass * 0.25, hp: 15e4 * armour, zoneId: id, visual: false, tags: ["frame", ...tags] });
11998
+ add({ id, role, name: label, material: "steel", box: floorPan(box2), mass: mass * 0.25, hp: 15e4 * armour, zoneId: id, visual: false, tags: ["frame", ...tags] });
10264
11999
  continue;
10265
12000
  }
10266
12001
  const roofLike = role === "roof";
10267
- const kg = (skin === "steel" ? STEEL_KG_M2 : ALU_KG_M2) * area * (role === "door" ? 1.6 : 1);
12002
+ const kg = (skin === "steel" ? STEEL_KG_M2 : ALU_KG_M2) * area3 * (role === "door" ? 1.6 : 1);
10268
12003
  add({
10269
12004
  id,
10270
12005
  role,
10271
12006
  name: label,
10272
12007
  material: skin,
10273
- box,
12008
+ box: box2,
10274
12009
  mass: Math.max(2, kg),
10275
12010
  hp: (roofLike ? 3e4 : 2e4) * armour,
10276
12011
  zoneId: id,
@@ -10492,7 +12227,7 @@ var GameVehicles = (() => {
10492
12227
  const my = kind === "bike" || kind === "quad" ? (m.top ?? belt + 0.25) - 0.1 : belt + 0.12;
10493
12228
  for (const s of [-1, 1]) {
10494
12229
  const door = zones.find((z) => z.type === "door" && z.side === s && (z.mirrors ?? []).length);
10495
- let box = { center: [mx, my, s * half], size: [0.14, 0.12, 0.2] };
12230
+ let box2 = { center: [mx, my, s * half], size: [0.14, 0.12, 0.2] };
10496
12231
  if (door) {
10497
12232
  const bb = door.mirrors.reduce(
10498
12233
  (acc, o) => {
@@ -10505,14 +12240,14 @@ var GameVehicles = (() => {
10505
12240
  },
10506
12241
  null
10507
12242
  );
10508
- if (bb) box = { center: [(bb[0] + bb[1]) / 2, (bb[2] + bb[3]) / 2, (bb[4] + bb[5]) / 2], size: [Math.max(0.06, bb[1] - bb[0]), Math.max(0.06, bb[3] - bb[2]), Math.max(0.06, bb[5] - bb[4])] };
12243
+ if (bb) box2 = { center: [(bb[0] + bb[1]) / 2, (bb[2] + bb[3]) / 2, (bb[4] + bb[5]) / 2], size: [Math.max(0.06, bb[1] - bb[0]), Math.max(0.06, bb[3] - bb[2]), Math.max(0.06, bb[5] - bb[4])] };
10509
12244
  }
10510
12245
  add({
10511
12246
  id: `mirror_${side(s)}`,
10512
12247
  role: "mirror",
10513
12248
  name: t("mechanics.mirror", { side: t(`sides.${side(s)}`), sideM: t(`sides.${side(s)}M`) }),
10514
12249
  material: "plastic",
10515
- box,
12250
+ box: box2,
10516
12251
  mass: 1.1,
10517
12252
  hp: 1500,
10518
12253
  ignitionC: 400,
@@ -10702,13 +12437,13 @@ var GameVehicles = (() => {
10702
12437
  (parent ?? this.c.vehicle).add(m);
10703
12438
  return m;
10704
12439
  }
10705
- surface(axis, sign, B, samples) {
12440
+ surface(axis, sign2, B, samples) {
10706
12441
  const V3 = this.THREE.Vector3;
10707
12442
  const hits = [];
10708
12443
  const far = 3;
10709
12444
  for (const [u, v] of samples) {
10710
- const o = axis === "z" ? new V3(u, v, sign * (Math.max(Math.abs(B.z0), Math.abs(B.z1)) + 1)) : new V3(sign * (Math.max(Math.abs(B.x0), Math.abs(B.x1)) + 1), u, v);
10711
- const d = axis === "z" ? new V3(0, 0, -sign) : new V3(-sign, 0, 0);
12445
+ const o = axis === "z" ? new V3(u, v, sign2 * (Math.max(Math.abs(B.z0), Math.abs(B.z1)) + 1)) : new V3(sign2 * (Math.max(Math.abs(B.x0), Math.abs(B.x1)) + 1), u, v);
12446
+ const d = axis === "z" ? new V3(0, 0, -sign2) : new V3(-sign2, 0, 0);
10712
12447
  const h = this.I.probe(this.c, o, d, far);
10713
12448
  if (h) hits.push(axis === "z" ? h.p.z : h.p.x);
10714
12449
  }
@@ -10858,12 +12593,12 @@ var GameVehicles = (() => {
10858
12593
  const z = this.zone(p);
10859
12594
  if (!z) return false;
10860
12595
  const V3 = this.THREE.Vector3;
10861
- const list = z.glass.filter((g) => g.parent && g.visible);
10862
- if (!list.length) return false;
12596
+ const list2 = z.glass.filter((g) => g.parent && g.visible);
12597
+ if (!list2.length) return false;
10863
12598
  const pt = new V3(...point);
10864
- let best = list[0];
12599
+ let best = list2[0];
10865
12600
  let bd = Infinity;
10866
- for (const g of list) {
12601
+ for (const g of list2) {
10867
12602
  g.geometry.computeBoundingBox();
10868
12603
  const bb = g.geometry.boundingBox.clone().applyMatrix4(g.matrix);
10869
12604
  const d = bb.distanceToPoint(pt);
@@ -11164,6 +12899,9 @@ var GameVehicles = (() => {
11164
12899
  const headless = !!options.headless;
11165
12900
  const seedBase = strHash(`${c.key}|${id}|${options.seed ?? 0}`);
11166
12901
  let rand = rng(seedBase);
12902
+ const ignitionRules = resolveIgnitionRules(options.ignition);
12903
+ const fuelFraction = clamp(options.fuel ?? 1, 0, 1);
12904
+ let igniteRand = rng(strHash(`${c.key}|${id}|ignition|${options.seed ?? 0}`));
11167
12905
  let clock = 0;
11168
12906
  let model;
11169
12907
  let vis = null;
@@ -11231,7 +12969,7 @@ var GameVehicles = (() => {
11231
12969
  dents: [],
11232
12970
  holes: [],
11233
12971
  cracks: [],
11234
- liters: d.contains?.liters,
12972
+ liters: d.contains ? d.role === "fuel_tank" ? d.contains.liters * fuelFraction : d.contains.liters : void 0,
11235
12973
  lost: false,
11236
12974
  status: "intact",
11237
12975
  flat: false,
@@ -11249,6 +12987,7 @@ var GameVehicles = (() => {
11249
12987
  wasLit = true;
11250
12988
  deadEngine = false;
11251
12989
  rand = rng(seedBase);
12990
+ igniteRand = rng(strHash(`${c.key}|${id}|ignition|${options.seed ?? 0}`));
11252
12991
  steerSign = rand() < 0.5 ? -1 : 1;
11253
12992
  vis = headless ? null : new VehicleVisuals(kit, c, model);
11254
12993
  recomputeGroups();
@@ -11270,8 +13009,8 @@ var GameVehicles = (() => {
11270
13009
  comps.get(r).push(p);
11271
13010
  }
11272
13011
  const hubRoot = parent.has(model.hub) ? find(model.hub) : "";
11273
- const list = [...comps.entries()].sort((a, b) => a[0] === hubRoot ? -1 : b[0] === hubRoot ? 1 : 0);
11274
- groupsCache = list.map(([, v]) => v);
13012
+ const list2 = [...comps.entries()].sort((a, b) => a[0] === hubRoot ? -1 : b[0] === hubRoot ? 1 : 0);
13013
+ groupsCache = list2.map(([, v]) => v);
11275
13014
  groupOf = /* @__PURE__ */ new Map();
11276
13015
  groupsCache.forEach((g, i) => g.forEach((p) => groupOf.set(p, i)));
11277
13016
  return groupsCache;
@@ -11543,8 +13282,19 @@ var GameVehicles = (() => {
11543
13282
  if (!p.burning && p.temp > AMBIENT) p.temp = Math.max(AMBIENT, p.temp - dt * 6);
11544
13283
  }
11545
13284
  for (const f of flames) heat(centreOf(f.def), 0.8, 500, dt * 0.5, out, 0);
13285
+ fireNearLeak(flames, dt, out);
11546
13286
  thermal(out);
11547
13287
  };
13288
+ const fireNearLeak = (flames, dt, out) => {
13289
+ const tank = tankPart();
13290
+ const leak = tank ? leaks.get(tank.def.id) : void 0;
13291
+ if (!tank || !leak || tank.lost || tank.burning || exploded || leak.fluid !== "fuel" && leak.fluid !== "diesel") return;
13292
+ if (leak.pud?.fire) return light(tank, out);
13293
+ const { radius, chancePerSecond } = ignitionRules.fireNear;
13294
+ if (chancePerSecond <= 0) return;
13295
+ const near = flames.some((f) => f !== tank && distToBox(tank.def, centreOf(f.def)) - Math.max(...f.def.box.size) / 2 <= radius);
13296
+ if (near && igniteRand() < 1 - Math.exp(-chancePerSecond * dt)) light(tank, out);
13297
+ };
11548
13298
  const normalOf = (d, v) => faceNormal(d, v);
11549
13299
  const ballistic = (h, out) => {
11550
13300
  sync();
@@ -11612,10 +13362,21 @@ var GameVehicles = (() => {
11612
13362
  let amount = through ? E * 0.6 : E * 0.25;
11613
13363
  if (through && (d.role === "battery" || d.role === "alternator")) amount = Math.max(amount, p.hp * 0.65);
11614
13364
  damage(p, amount, "ballistic", h.source, out, worldPoint);
11615
- if (d.role === "fuel_tank" && E > 1500 && (p.liters ?? 0) > 1) p.temp = Math.max(p.temp, d.ignitionC ?? 260);
13365
+ if (through && d.role === "fuel_tank") rollIgnition(p, h, metal, out);
11616
13366
  }
11617
13367
  }
11618
13368
  };
13369
+ const light = (tank, out) => {
13370
+ tank.temp = Math.max(tank.temp, (tank.def.ignitionC ?? 260) + 1);
13371
+ if ((tank.liters ?? 0) > 0.5) ignite(tank, out);
13372
+ };
13373
+ const rollIgnition = (tank, h, metal, out) => {
13374
+ const d = tank.def;
13375
+ if (!d.contains || tank.burning || (tank.liters ?? 0) <= 0.05) return;
13376
+ const fill = d.contains.liters > 0 ? (tank.liters ?? 0) / d.contains.liters : 0;
13377
+ const chance = ignitionChance(h, { sparkTarget: sparkTargetFactor(d.material, metal, ignitionRules), fill, fluid: d.contains.fluid }, ignitionRules);
13378
+ if (chance > 0 && igniteRand() < chance) light(tank, out);
13379
+ };
11619
13380
  const blunt = (h, out) => {
11620
13381
  sync();
11621
13382
  const M = toFrameMatrix();
@@ -11679,7 +13440,7 @@ var GameVehicles = (() => {
11679
13440
  const verdict = kit.blastDamage(c, E / (1 + dist * dist * 4) / 50);
11680
13441
  if (verdict === "explosion") explode(tank, h.source, out);
11681
13442
  else if (verdict === "fire") {
11682
- tank.temp = Math.max(tank.temp, tank.def.ignitionC ?? 260);
13443
+ light(tank, out);
11683
13444
  startLeak(tank, centreOf(tank.def).setY(tank.def.box.center[1] - tank.def.box.size[1] / 2 + 0.05), 12, out, dir);
11684
13445
  }
11685
13446
  }
@@ -11693,9 +13454,9 @@ var GameVehicles = (() => {
11693
13454
  thermal(out);
11694
13455
  };
11695
13456
  const sound = (role) => {
11696
- const list = [...ps.values()].filter((p) => p.def.role === role);
11697
- if (!list.length) return true;
11698
- return list.every((p) => !p.lost && attached(p.def.id) && p.hp >= p.def.hp * 0.5);
13457
+ const list2 = [...ps.values()].filter((p) => p.def.role === role);
13458
+ if (!list2.length) return true;
13459
+ return list2.every((p) => !p.lost && attached(p.def.id) && p.hp >= p.def.hp * 0.5);
11699
13460
  };
11700
13461
  const tankPart = () => [...ps.values()].find((p) => p.def.role === "fuel_tank" && p.def.contains);
11701
13462
  const computeStatus = () => {
@@ -11887,8 +13648,8 @@ var GameVehicles = (() => {
11887
13648
  queue = [];
11888
13649
  const hood = c.hood;
11889
13650
  if (hood) {
11890
- const sign = hood.sign || 1;
11891
- const cur = hood.piv.rotation.z / sign;
13651
+ const sign2 = hood.sign || 1;
13652
+ const cur = hood.piv.rotation.z / sign2;
11892
13653
  if (Math.abs(cur - hood.ang) > 1e-4) {
11893
13654
  hood.ang = cur;
11894
13655
  hood.target = cur;
@@ -11998,7 +13759,7 @@ var GameVehicles = (() => {
11998
13759
  // src/destructible/item.ts
11999
13760
  function buildVehicleItem(kit, key, options = {}) {
12000
13761
  const THREE = kit.THREE;
12001
- const vehicle = kit.build(key, options.state ?? "intact");
13762
+ const vehicle = options.lod ? kit.build(key, options.state ?? "intact", void 0, { lod: true }) : kit.build(key, options.state ?? "intact");
12002
13763
  const m = vehicle.meta;
12003
13764
  const root = new THREE.Group();
12004
13765
  root.name = key;