@adatechnology/game-vehicles 0.2.0 → 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.
@@ -51,6 +51,1523 @@ var GameVehicles = (() => {
51
51
  wrapThreeForLod: () => wrapThreeForLod
52
52
  });
53
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
+
54
1571
  // src/locales/en.json
55
1572
  var en_default = {
56
1573
  plate: {
@@ -746,9 +2263,9 @@ var GameVehicles = (() => {
746
2263
  return envTex;
747
2264
  }
748
2265
  kit.studioEnvironment = studioEnvironment;
749
- function makeMaterial(color, o) {
2266
+ function makeMaterial(color2, o) {
750
2267
  o = o || {};
751
- const c = color instanceof THREE.Color ? color : new THREE.Color(color);
2268
+ const c = color2 instanceof THREE.Color ? color2 : new THREE.Color(color2);
752
2269
  const key = "s" + c.getHexString() + JSON.stringify(o, function(k, v) {
753
2270
  return v && v.isColor ? "#" + v.getHexString() : v && v.isTexture ? v.uuid : v;
754
2271
  });
@@ -764,8 +2281,8 @@ var GameVehicles = (() => {
764
2281
  }
765
2282
  return cache[key];
766
2283
  }
767
- function makeFlatMaterial(color, o) {
768
- 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 || {}));
769
2286
  }
770
2287
  const DOUBLE_SIDE = THREE.DoubleSide;
771
2288
  function toColor(hex) {
@@ -1013,10 +2530,10 @@ var GameVehicles = (() => {
1013
2530
  const s = new V3();
1014
2531
  for (let i = 0; i < n; i++) {
1015
2532
  const fi = i / 3 | 0;
1016
- const list = map[keys[i]];
2533
+ const list2 = map[keys[i]];
1017
2534
  s.set(0, 0, 0);
1018
- for (let j = 0; j < list.length; j++) {
1019
- const g = FN[list[j]];
2535
+ for (let j = 0; j < list2.length; j++) {
2536
+ const g = FN[list2[j]];
1020
2537
  if (g.dot(FN[fi]) >= cs) s.add(g);
1021
2538
  }
1022
2539
  if (s.lengthSq() < 1e-20) s.copy(FN[fi]);
@@ -1082,7 +2599,7 @@ var GameVehicles = (() => {
1082
2599
  const pos = [];
1083
2600
  const col = [];
1084
2601
  const idx = [];
1085
- const ring = seg + 1;
2602
+ const ring2 = seg + 1;
1086
2603
  st.forEach(function(s) {
1087
2604
  for (let j = 0; j <= seg; j++) {
1088
2605
  const a = j / seg * PI * 2;
@@ -1100,8 +2617,8 @@ var GameVehicles = (() => {
1100
2617
  });
1101
2618
  for (let i = 0; i < st.length - 1; i++)
1102
2619
  for (let k = 0; k < seg; k++) {
1103
- const a0 = i * ring + k;
1104
- const b0 = a0 + ring;
2620
+ const a0 = i * ring2 + k;
2621
+ const b0 = a0 + ring2;
1105
2622
  idx.push(a0, a0 + 1, b0, a0 + 1, b0 + 1, b0);
1106
2623
  }
1107
2624
  ;
@@ -1115,7 +2632,7 @@ var GameVehicles = (() => {
1115
2632
  col.push(cc.r, cc.g, cc.b);
1116
2633
  }
1117
2634
  for (let k = 0; k < seg; k++) {
1118
- const a = si * ring + k;
2635
+ const a = si * ring2 + k;
1119
2636
  if (e === 0) idx.push(ci, a + 1, a);
1120
2637
  else idx.push(ci, a, a + 1);
1121
2638
  }
@@ -1253,13 +2770,13 @@ var GameVehicles = (() => {
1253
2770
  });
1254
2771
  tris = next;
1255
2772
  });
1256
- function build(list) {
2773
+ function build(list2) {
1257
2774
  const g = new THREE.BufferGeometry();
1258
2775
  let off = 0;
1259
2776
  names.forEach(function(nm, a) {
1260
- const arr = new Float32Array(list.length * 3 * sizes[a]);
2777
+ const arr = new Float32Array(list2.length * 3 * sizes[a]);
1261
2778
  let w = 0;
1262
- list.forEach(function(t) {
2779
+ list2.forEach(function(t) {
1263
2780
  for (let k = 0; k < 3; k++) for (let q = 0; q < sizes[a]; q++) arr[w++] = t[k][off + q];
1264
2781
  });
1265
2782
  g.setAttribute(nm, new THREE.BufferAttribute(arr, sizes[a]));
@@ -1274,10 +2791,10 @@ var GameVehicles = (() => {
1274
2791
  });
1275
2792
  return out;
1276
2793
  }
1277
- function mergeGeometries(list) {
2794
+ function mergeGeometries(list2) {
1278
2795
  const pos = [];
1279
2796
  const nor = [];
1280
- list.forEach(function(g2) {
2797
+ list2.forEach(function(g2) {
1281
2798
  const n = g2.index ? g2.toNonIndexed() : g2;
1282
2799
  if (!n.attributes.normal) n.computeVertexNormals();
1283
2800
  const pa = n.attributes.position.array;
@@ -1336,13 +2853,13 @@ var GameVehicles = (() => {
1336
2853
  };
1337
2854
  kit.holeSide = function(poly, o) {
1338
2855
  o = o || {};
1339
- let area = 0;
2856
+ let area3 = 0;
1340
2857
  for (let i = 0; i < poly.length; i++) {
1341
2858
  const a = poly[i];
1342
2859
  const b = poly[(i + 1) % poly.length];
1343
- area += a[0] * b[1] - b[0] * a[1];
2860
+ area3 += a[0] * b[1] - b[0] * a[1];
1344
2861
  }
1345
- const sg = area > 0 ? 1 : -1;
2862
+ const sg = area3 > 0 ? 1 : -1;
1346
2863
  const E2 = poly.map(function(a, i) {
1347
2864
  const b = poly[(i + 1) % poly.length];
1348
2865
  const dx = b[0] - a[0];
@@ -1645,11 +3162,11 @@ var GameVehicles = (() => {
1645
3162
  return d.clone().applyQuaternion(this.vehicle.quaternion);
1646
3163
  }
1647
3164
  /** Declares or refines a damage zone: `c.zone(id, label, box, parts, options)`. Pass `label === false` to remove an auto-derived zone. */
1648
- zone(id, label, box, parts, o) {
3165
+ zone(id, label, box2, parts, o) {
1649
3166
  const d = Object.assign({ id }, o || {});
1650
3167
  if (label === false) d.remove = true;
1651
3168
  else if (label) d.name = label;
1652
- if (box) d.box = I.normalizeBox(box);
3169
+ if (box2) d.box = I.normalizeBox(box2);
1653
3170
  if (parts) d.objs = Array.isArray(parts) ? parts.slice() : [parts];
1654
3171
  this.zoneDefs.push(d);
1655
3172
  return d;
@@ -1800,7 +3317,7 @@ var GameVehicles = (() => {
1800
3317
  const seg = o.seg || 24;
1801
3318
  const pos = [];
1802
3319
  const idx = [];
1803
- const ring = seg + 1;
3320
+ const ring2 = seg + 1;
1804
3321
  const axisY = o.axis === "y";
1805
3322
  st.forEach(function(s) {
1806
3323
  const a0 = s[1];
@@ -1822,8 +3339,8 @@ var GameVehicles = (() => {
1822
3339
  });
1823
3340
  for (let i = 0; i < st.length - 1; i++)
1824
3341
  for (let k = 0; k < seg; k++) {
1825
- const A = i * ring + k;
1826
- const B = A + ring;
3342
+ const A = i * ring2 + k;
3343
+ const B = A + ring2;
1827
3344
  if (axisY) idx.push(A, B, A + 1, A + 1, B, B + 1);
1828
3345
  else idx.push(A, A + 1, B, A + 1, B + 1, B);
1829
3346
  }
@@ -1836,7 +3353,7 @@ var GameVehicles = (() => {
1836
3353
  if (axisY) pos.push(cc, s[0], s[5] || 0);
1837
3354
  else pos.push(s[0], cc, s[5] || 0);
1838
3355
  for (let k = 0; k < seg; k++) {
1839
- const a = si * ring + k;
3356
+ const a = si * ring2 + k;
1840
3357
  const flip = e === 0 !== axisY;
1841
3358
  if (flip) idx.push(ci, a + 1, a);
1842
3359
  else idx.push(ci, a, a + 1);
@@ -2068,15 +3585,15 @@ var GameVehicles = (() => {
2068
3585
  c.part("glass", L, m, pos, o.reach || 0.9);
2069
3586
  return m;
2070
3587
  };
2071
- kit.asGlass = function(c, mesh, label, type, o) {
3588
+ kit.asGlass = function(c, mesh2, label, type, o) {
2072
3589
  o = o || {};
2073
- const i = c.deform.indexOf(mesh);
3590
+ const i = c.deform.indexOf(mesh2);
2074
3591
  if (i >= 0) c.deform.splice(i, 1);
2075
- if (mesh.parent) mesh.parent.remove(mesh);
2076
- const geo = mesh.geometry;
2077
- 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));
2078
3595
  if (o.reuv !== false) o.reuv = true;
2079
- 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));
2080
3597
  };
2081
3598
  kit.sideWindows = function(c, polys, Wg, T, label, o) {
2082
3599
  o = o || {};
@@ -2122,12 +3639,12 @@ var GameVehicles = (() => {
2122
3639
  });
2123
3640
  });
2124
3641
  };
2125
- kit.cutMesh = function(mesh, tests, deg, o) {
2126
- 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;
2127
3644
  const r = cutGeometry(src, tests);
2128
3645
  crease(r.geo, deg || 42);
2129
- mesh.geometry.dispose();
2130
- mesh.geometry = r.geo;
3646
+ mesh2.geometry.dispose();
3647
+ mesh2.geometry = r.geo;
2131
3648
  return r.cut;
2132
3649
  };
2133
3650
  kit.quadDef = function(pb, pt, Wg, T, which, bev, o) {
@@ -2327,8 +3844,8 @@ var GameVehicles = (() => {
2327
3844
  const pz = nR === 1 ? 0 : (i ? 1 : -1) * w / 4;
2328
3845
  const back = gm(g, new THREE.CylinderGeometry(r * 1.06, r * 1.06, d * 0.8, 22), MATERIALS.housing, 0, 0, pz);
2329
3846
  back.rotation.z = PI / 2;
2330
- const ring = gm(g, new THREE.TorusGeometry(r * 1.02, 0.013, 8, 30), MATERIALS.chrome, d / 2 - 6e-3, 0, pz);
2331
- 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;
2332
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);
2333
3850
  bowl.rotation.z = PI / 2;
2334
3851
  bowl.scale.y = 0.55;
@@ -2405,8 +3922,8 @@ var GameVehicles = (() => {
2405
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);
2406
3923
  e.rotation.z = PI / 2;
2407
3924
  inner.push(e);
2408
- const ring = gm(g, new THREE.TorusGeometry(r, 0.01, 6, 24), MATERIALS.chrome, d / 2 - 4e-3, 0, pz);
2409
- 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;
2410
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);
2411
3928
  ln.rotation.z = -PI / 2;
2412
3929
  ln.scale.y = 0.25;
@@ -2499,7 +4016,7 @@ var GameVehicles = (() => {
2499
4016
  } else if (st === "mesh") {
2500
4017
  gm(g, roundedBoxGeometry(0.05, h, w, Math.min(0.03, h * 0.3)), MATERIALS.housing);
2501
4018
  const cell = 0.026;
2502
- const list = [];
4019
+ const list2 = [];
2503
4020
  const rows = Math.floor(h * 0.85 / (cell * 1.6));
2504
4021
  const cols = Math.floor(w * 0.9 / (cell * 1.8));
2505
4022
  for (let rI = 0; rI < rows; rI++)
@@ -2508,9 +4025,9 @@ var GameVehicles = (() => {
2508
4025
  hg.rotateZ(PI / 2);
2509
4026
  hg.rotateX(PI / 6);
2510
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);
2511
- list.push(hg);
4028
+ list2.push(hg);
2512
4029
  }
2513
- if (list.length) gm(g, mergeGeometries(list), MATERIALS.trim);
4030
+ if (list2.length) gm(g, mergeGeometries(list2), MATERIALS.trim);
2514
4031
  gm(g, roundedBoxGeometry(0.022, 0.014, w, 6e-3), MATERIALS.chrome, 0.03, h / 2 - 8e-3, 0);
2515
4032
  } else if (st === "big") {
2516
4033
  const fr = roundedBoxGeometry(0.06, h, w, 0.03);
@@ -3351,7 +4868,7 @@ var GameVehicles = (() => {
3351
4868
  m.underbodyGroup = ug;
3352
4869
  const DK = makeMaterial("#1a1b1c", { roughness: 0.75, metalness: 0.3 });
3353
4870
  const ST = MATERIALS.steel;
3354
- function box(w, h, d, mat, x, y, z) {
4871
+ function box2(w, h, d, mat, x, y, z) {
3355
4872
  return c.roundBox(w, h, d, Math.min(0.02, h * 0.4), mat, x, y, z, { parent: ug });
3356
4873
  }
3357
4874
  function rod(a, b, r, mat) {
@@ -3368,9 +4885,9 @@ var GameVehicles = (() => {
3368
4885
  rod(new V3(rx, rr, -zW), new V3(rx, rr, zW), 0.045, ST);
3369
4886
  c.sphere(0.13, ST, rx, rr, 0, 1, 0.85, 1, { parent: ug });
3370
4887
  rod(new V3(rx + 0.1, rr, 0), new V3(fx - 0.6, yU + 0.04, 0), 0.035, ST);
3371
- } 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);
3372
4889
  if (!ev) {
3373
- 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);
3374
4891
  const tz = W * 0.3;
3375
4892
  const mx = rx - 0.15;
3376
4893
  const ty = Math.max(0.14, by - 0.06);
@@ -3387,10 +4904,10 @@ var GameVehicles = (() => {
3387
4904
  { parent: ug, seg: 30, rs: 6 }
3388
4905
  );
3389
4906
  c.cylinder(0.1, 0.1, 0.42, ST, mx, ty + 0.02, tz, "x", { parent: ug });
3390
- box(0.24, 0.06, 0.2, DK, (fx + rx) / 2 + 0.1, ty, 0.16);
3391
- 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);
3392
4909
  } else {
3393
- box(
4910
+ box2(
3394
4911
  (fx - rx) * 0.72,
3395
4912
  0.1,
3396
4913
  W - 0.4,
@@ -3399,7 +4916,7 @@ var GameVehicles = (() => {
3399
4916
  Math.max(0.14, by - 0.03),
3400
4917
  0
3401
4918
  );
3402
- 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);
3403
4920
  }
3404
4921
  if (o.spare && m.rear < rx - 0.7) {
3405
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 });
@@ -3727,16 +5244,16 @@ var GameVehicles = (() => {
3727
5244
  }
3728
5245
  } else {
3729
5246
  const ns = o.spokes || 28;
3730
- const list = [];
5247
+ const list2 = [];
3731
5248
  for (let jj = 0; jj < ns; jj++) {
3732
5249
  const a2 = jj / ns * PI * 2;
3733
5250
  const cg = new THREE.CylinderGeometry(25e-4, 25e-4, sl, 4);
3734
5251
  cg.applyMatrix4(
3735
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)
3736
5253
  );
3737
- list.push(cg);
5254
+ list2.push(cg);
3738
5255
  }
3739
- g.add(new THREE.Mesh(mergeGeometries(list), MATERIALS.chrome));
5256
+ g.add(new THREE.Mesh(mergeGeometries(list2), MATERIALS.chrome));
3740
5257
  }
3741
5258
  const hub = new THREE.Mesh(new THREE.CylinderGeometry(0.035, 0.035, 0.1, 12), MATERIALS.steel);
3742
5259
  hub.rotation.x = PI / 2;
@@ -3918,9 +5435,9 @@ var GameVehicles = (() => {
3918
5435
  return puffTexture(k * 17, true);
3919
5436
  });
3920
5437
  }
3921
- const list = fire ? FIRE_TEXTURES : SMOKE_TEXTURES;
5438
+ const list2 = fire ? FIRE_TEXTURES : SMOKE_TEXTURES;
3922
5439
  const mat = new THREE.SpriteMaterial({
3923
- map: list[Math.floor(Math.random() * list.length)],
5440
+ map: list2[Math.floor(Math.random() * list2.length)],
3924
5441
  color: new THREE.Color(col),
3925
5442
  transparent: true,
3926
5443
  depthWrite: false,
@@ -5229,7 +6746,7 @@ var GameVehicles = (() => {
5229
6746
  }
5230
6747
  });
5231
6748
  const dset = new Set(c.deform);
5232
- const box = new THREE.Box3();
6749
+ const box2 = new THREE.Box3();
5233
6750
  c.vehicle.children.forEach(function(o) {
5234
6751
  if (dset.has(o) || o.userData.noFollow) return;
5235
6752
  if (!(o.isMesh || o.isGroup && o.userData.rigid)) return;
@@ -5240,10 +6757,10 @@ var GameVehicles = (() => {
5240
6757
  if (!o.geometry.boundingBox) o.geometry.computeBoundingBox();
5241
6758
  o.userData.bb0 = o.geometry.boundingBox.clone().applyMatrix4(o.matrix);
5242
6759
  } else {
5243
- box.setFromObject(o);
5244
- box.applyMatrix4(gInv);
5245
- o.userData.c0 = box.getCenter(new V3());
5246
- 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();
5247
6764
  }
5248
6765
  o.userData.pos0 = o.position.clone();
5249
6766
  }
@@ -5539,11 +7056,11 @@ var GameVehicles = (() => {
5539
7056
  [12, "#d8322a", kit.t("blast.lethal", { m: 12 })],
5540
7057
  [30, "#e0a548", kit.t("blast.injury", { m: 30 })]
5541
7058
  ].forEach(function(r) {
5542
- const ring = new THREE.Mesh(new THREE.RingGeometry(r[0] - 0.25, r[0], 96), boomMat(r[1], 0.85));
5543
- ring.rotation.x = -PI / 2;
5544
- ring.position.y = 0.06;
5545
- ring.renderOrder = 5;
5546
- 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);
5547
7064
  const fill = new THREE.Mesh(new THREE.CircleGeometry(r[0], 96), boomMat(r[1], 0.07));
5548
7065
  fill.rotation.x = -PI / 2;
5549
7066
  fill.position.y = 0.05;
@@ -5560,7 +7077,7 @@ var GameVehicles = (() => {
5560
7077
  lb.rotation.x = -PI / 2;
5561
7078
  lb.position.set(0, 0.08, r[0] + r[0] * 0.06);
5562
7079
  g.add(lb);
5563
- c.bigFx.rings.push(ring, fill, lb);
7080
+ c.bigFx.rings.push(ring2, fill, lb);
5564
7081
  });
5565
7082
  }
5566
7083
  function detonate(c) {
@@ -5835,7 +7352,7 @@ var GameVehicles = (() => {
5835
7352
  const I = kit._;
5836
7353
  const { MATERIALS } = I;
5837
7354
  function rollMeshes(c) {
5838
- const list = [];
7355
+ const list2 = [];
5839
7356
  const thin = [];
5840
7357
  c.root.updateMatrixWorld(true);
5841
7358
  const inv = new THREE.Matrix4().copy(c.vehicle.matrixWorld).invert();
@@ -5853,9 +7370,9 @@ var GameVehicles = (() => {
5853
7370
  thin.push({ o: ob, M });
5854
7371
  return;
5855
7372
  }
5856
- list.push({ o: ob, M });
7373
+ list2.push({ o: ob, M });
5857
7374
  });
5858
- return { list, thin };
7375
+ return { list: list2, thin };
5859
7376
  }
5860
7377
  function convexHull(pts) {
5861
7378
  pts.sort(function(a, b) {
@@ -5938,7 +7455,7 @@ var GameVehicles = (() => {
5938
7455
  };
5939
7456
  function rollContact(c, A, q) {
5940
7457
  const m = c.meta;
5941
- const list = [];
7458
+ const list2 = [];
5942
7459
  const dy = A.cen[0] - q[0];
5943
7460
  const dz = A.cen[1] - q[1];
5944
7461
  const dl = Math.hypot(dy, dz) || 1;
@@ -5946,17 +7463,17 @@ var GameVehicles = (() => {
5946
7463
  const f = 0.75;
5947
7464
  const bike = m.kind === "bike";
5948
7465
  (bike ? [0] : [-0.28, 0, 0.28]).forEach(function(k) {
5949
- 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]);
5950
7467
  });
5951
7468
  if (!bike && Math.abs(dir.z) > 0.75 && m.top - m.belt > 0.25 && A.hits < 3) {
5952
7469
  const sd = -Math.sign(dir.z);
5953
7470
  const yw = m.belt + (m.top - m.belt) * 0.45;
5954
7471
  [-0.25, 0.2].forEach(function(k) {
5955
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);
5956
- if (h) list.push([h.p, new V3(0, 0, -sd)]);
7473
+ if (h) list2.push([h.p, new V3(0, 0, -sd)]);
5957
7474
  });
5958
7475
  }
5959
- list.forEach(function(e) {
7476
+ list2.forEach(function(e) {
5960
7477
  I.impact(c, e[0], e[1], f, { noStain: true, maxDepth: 0.1 });
5961
7478
  const pf = c.toEffects(e[0]);
5962
7479
  pf.y = Math.max(0.03, pf.y);
@@ -5966,15 +7483,15 @@ var GameVehicles = (() => {
5966
7483
  });
5967
7484
  A.hits++;
5968
7485
  A.bounce = A.hits === 1 ? 0.06 : 0.1;
5969
- 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 });
5970
7487
  }
5971
- function exactMinY(c, list) {
7488
+ function exactMinY(c, list2) {
5972
7489
  c.root.updateMatrixWorld(true);
5973
7490
  const inv = new THREE.Matrix4().copy(c.root.matrixWorld).invert();
5974
7491
  const M = new THREE.Matrix4();
5975
7492
  const v = new V3();
5976
7493
  let mn = 1e9;
5977
- list.forEach(function(e) {
7494
+ list2.forEach(function(e) {
5978
7495
  const ob = e.o;
5979
7496
  if (!ob.parent || !I.shown(c, ob)) return;
5980
7497
  M.multiplyMatrices(inv, ob.matrixWorld);
@@ -8170,9 +9687,9 @@ var GameVehicles = (() => {
8170
9687
  down: new V3(0, 1, 0)
8171
9688
  }[d] || null;
8172
9689
  }
8173
- function resolveObjs(c, list, z) {
9690
+ function resolveObjs(c, list2, z) {
8174
9691
  const out = [];
8175
- (list || []).forEach(function(o) {
9692
+ (list2 || []).forEach(function(o) {
8176
9693
  if (!o) return;
8177
9694
  if (typeof o === "string") {
8178
9695
  c.parts.forEach(function(p) {
@@ -8200,22 +9717,22 @@ var GameVehicles = (() => {
8200
9717
  }
8201
9718
  function deriveZones(c) {
8202
9719
  const m = c.meta;
8203
- let list = [];
9720
+ let list2 = [];
8204
9721
  const byId = {};
8205
9722
  (m.zones === false ? [] : I.autoZones(c)).forEach(function(z) {
8206
9723
  byId[z.id] = z;
8207
- list.push(z);
9724
+ list2.push(z);
8208
9725
  });
8209
9726
  (c.zoneDefs || []).forEach(function(d) {
8210
9727
  let z = byId[d.id];
8211
9728
  if (d.remove) {
8212
9729
  if (z) {
8213
- list.splice(list.indexOf(z), 1);
9730
+ list2.splice(list2.indexOf(z), 1);
8214
9731
  delete byId[d.id];
8215
9732
  }
8216
- list.slice().forEach(function(q) {
9733
+ list2.slice().forEach(function(q) {
8217
9734
  if (q.id.indexOf(d.id + "_") === 0 && /^\d+$/.test(q.id.slice(d.id.length + 1))) {
8218
- list.splice(list.indexOf(q), 1);
9735
+ list2.splice(list2.indexOf(q), 1);
8219
9736
  delete byId[q.id];
8220
9737
  }
8221
9738
  });
@@ -8228,7 +9745,7 @@ var GameVehicles = (() => {
8228
9745
  } else {
8229
9746
  z = Object.assign({ type: "panel" }, d);
8230
9747
  byId[d.id] = z;
8231
- list.push(z);
9748
+ list2.push(z);
8232
9749
  }
8233
9750
  });
8234
9751
  const inv = I.worldInverseOf(c);
@@ -8261,10 +9778,10 @@ var GameVehicles = (() => {
8261
9778
  }
8262
9779
  z.box = I.normalizeBox(bb);
8263
9780
  });
8264
- list = list.filter(function(z) {
9781
+ list2 = list2.filter(function(z) {
8265
9782
  return z.box && z.name;
8266
9783
  });
8267
- list.forEach(function(z) {
9784
+ list2.forEach(function(z) {
8268
9785
  z.glass = (z.glass || []).slice();
8269
9786
  z.lights = (z.lights || []).slice();
8270
9787
  z.wheels = (z.wheels || []).slice();
@@ -8301,7 +9818,7 @@ var GameVehicles = (() => {
8301
9818
  if (owner.has(g) || !g.parent || !g.userData.glass || g.userData.ghost) return;
8302
9819
  const cg = I.localBox(c, g, inv).getCenter(new V3());
8303
9820
  let best = null;
8304
- list.forEach(function(z) {
9821
+ list2.forEach(function(z) {
8305
9822
  if (z.internal || z.type === "wheel" || z.type === "light") return;
8306
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;
8307
9824
  });
@@ -8310,7 +9827,7 @@ var GameVehicles = (() => {
8310
9827
  owner.set(g, best);
8311
9828
  }
8312
9829
  });
8313
- const looseParts = list.filter(function(z) {
9830
+ const looseParts = list2.filter(function(z) {
8314
9831
  return z.type === "part";
8315
9832
  });
8316
9833
  if (looseParts.length)
@@ -8335,7 +9852,7 @@ var GameVehicles = (() => {
8335
9852
  });
8336
9853
  }
8337
9854
  });
8338
- list.forEach(function(z) {
9855
+ list2.forEach(function(z) {
8339
9856
  if (!z.mirror) return;
8340
9857
  z.mirrors = [];
8341
9858
  const B = z.box;
@@ -8367,21 +9884,21 @@ var GameVehicles = (() => {
8367
9884
  });
8368
9885
  }
8369
9886
  });
8370
- list.forEach(function(z) {
9887
+ list2.forEach(function(z) {
8371
9888
  const B = z.box;
8372
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)];
8373
9890
  });
8374
- carMap(c, list);
9891
+ carMap(c, list2);
8375
9892
  c.zoneOwners = owner;
8376
- return list;
9893
+ return list2;
8377
9894
  }
8378
- function carMap(c, list) {
9895
+ function carMap(c, list2) {
8379
9896
  const m = c.meta;
8380
9897
  const W = m.width || 1.7;
8381
9898
  const hw = W / 2;
8382
9899
  const gh = m.glasshouse;
8383
9900
  if ((m.kind || "car") !== "car" || !gh || gh.fb == null) return;
8384
- list.forEach(function(z) {
9901
+ list2.forEach(function(z) {
8385
9902
  if (z.mapSet) return;
8386
9903
  const B = z.box;
8387
9904
  const s = z.side;
@@ -8446,8 +9963,8 @@ var GameVehicles = (() => {
8446
9963
  const top = m.top || belt + 0.5;
8447
9964
  const hw = W / 2;
8448
9965
  const WH = I.wheelInfo(c);
8449
- function add(id, name, type, box, o) {
8450
- 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 || {});
8451
9968
  Z.push(z);
8452
9969
  return z;
8453
9970
  }
@@ -9951,9 +11468,9 @@ var GameVehicles = (() => {
9951
11468
  const kind = kindOf2(zone);
9952
11469
  if (!kind || !zone.box) continue;
9953
11470
  const b = zone.box;
9954
- const box = new THREE.Box3(new THREE.Vector3(b.x0, b.y0, b.z0), new THREE.Vector3(b.x1, b.y1, b.z1));
9955
- if (matrix) box.applyMatrix4(matrix);
9956
- out.push({ material: kind.material, zone: zone.id, part: kind.part, side: zone.side === 0 ? "center" : zone.side < 0 ? "left" : "right", 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 });
9957
11474
  }
9958
11475
  return out;
9959
11476
  };
@@ -9964,7 +11481,7 @@ var GameVehicles = (() => {
9964
11481
  const opts = options || {};
9965
11482
  if (!opts.THREE || !opts.THREE.Vector3) throw new Error("createVehicleKit: options.THREE (the three.js namespace) is required");
9966
11483
  const lod = createLod(opts.lod);
9967
- const THREE = wrapThreeForLod(opts.THREE, lod);
11484
+ const THREE = wrapThreeForLod(shapeGeometries(opts.THREE), lod);
9968
11485
  const strings = resolveStrings(opts.locale);
9969
11486
  const t = createTranslate(strings);
9970
11487
  const doc = opts.document || (typeof document !== "undefined" ? document : void 0);
@@ -10225,6 +11742,51 @@ var GameVehicles = (() => {
10225
11742
  return kit;
10226
11743
  }
10227
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
+
10228
11790
  // src/destructible/model.ts
10229
11791
  var JOINT_RULES = {
10230
11792
  hinge: { strength: 5e3, tools: ["wrench", "screwdriver"], seconds: 3 },
@@ -10352,19 +11914,19 @@ var GameVehicles = (() => {
10352
11914
  for (const z of zones) {
10353
11915
  let role = roleOfZone(z);
10354
11916
  if (z === hubZone) role = "frame";
10355
- const box = boxOf(z);
10356
- const area = areaOf(box.size);
11917
+ const box2 = boxOf(z);
11918
+ const area3 = areaOf(box2.size);
10357
11919
  const id = z.id;
10358
11920
  const label = String(z.name ?? id);
10359
11921
  const tags = [...z.tags ?? []];
10360
11922
  if (role === "rim") {
10361
11923
  const wheels = z.wheels ?? [];
10362
11924
  const r = Math.max(0.2, ...wheels.map((w) => w.userData?.r ?? 0.3));
10363
- const sz = Math.sign(box.center[2]) || 1;
10364
- 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] };
10365
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] });
10366
11928
  const tyreId = id.replace(/^wheels?/, "tyre").replace(/^(?!tyre)/, "tyre_");
10367
- 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]] };
10368
11930
  add({
10369
11931
  id: tyreId,
10370
11932
  role: "tyre",
@@ -10384,29 +11946,29 @@ var GameVehicles = (() => {
10384
11946
  if (role === "glass") {
10385
11947
  const glass = (z.glass ?? [])[0];
10386
11948
  const laminated = glass?.userData?.glass?.type === "laminated" || m.bullet;
10387
- 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] });
10388
11950
  continue;
10389
11951
  }
10390
11952
  if (role === "light") {
10391
- 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] });
10392
11954
  continue;
10393
11955
  }
10394
11956
  if (role === "bumper") {
10395
- 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] });
10396
11958
  continue;
10397
11959
  }
10398
11960
  if (role === "engine") {
10399
11961
  const kg2 = { bike: 55, quad: 90, car: 150, tractor: 450, truck: 600, bus: 450, tank: 900, boat: 90, plane: 160, heli: 150 }[kind];
10400
- 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] });
10401
11963
  continue;
10402
11964
  }
10403
11965
  if (role === "radiator") {
10404
- 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] });
10405
11967
  continue;
10406
11968
  }
10407
11969
  if (role === "fuel_tank") {
10408
11970
  if (fuelless) {
10409
- 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 });
10410
11972
  continue;
10411
11973
  }
10412
11974
  const liters = { bike: 14, quad: 16, car: 55, tractor: 150, truck: 300, bus: 200, tank: 600, boat: 90, plane: 120, heli: 300 }[kind];
@@ -10416,7 +11978,7 @@ var GameVehicles = (() => {
10416
11978
  role,
10417
11979
  name: label,
10418
11980
  material: "steel",
10419
- box,
11981
+ box: box2,
10420
11982
  mass: 14 + liters * 0.8,
10421
11983
  hp: 25e3 * armour,
10422
11984
  zoneId: id,
@@ -10429,21 +11991,21 @@ var GameVehicles = (() => {
10429
11991
  continue;
10430
11992
  }
10431
11993
  if (role === "battery") {
10432
- 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] });
10433
11995
  continue;
10434
11996
  }
10435
11997
  if (role === "frame") {
10436
- 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] });
10437
11999
  continue;
10438
12000
  }
10439
12001
  const roofLike = role === "roof";
10440
- 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);
10441
12003
  add({
10442
12004
  id,
10443
12005
  role,
10444
12006
  name: label,
10445
12007
  material: skin,
10446
- box,
12008
+ box: box2,
10447
12009
  mass: Math.max(2, kg),
10448
12010
  hp: (roofLike ? 3e4 : 2e4) * armour,
10449
12011
  zoneId: id,
@@ -10665,7 +12227,7 @@ var GameVehicles = (() => {
10665
12227
  const my = kind === "bike" || kind === "quad" ? (m.top ?? belt + 0.25) - 0.1 : belt + 0.12;
10666
12228
  for (const s of [-1, 1]) {
10667
12229
  const door = zones.find((z) => z.type === "door" && z.side === s && (z.mirrors ?? []).length);
10668
- 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] };
10669
12231
  if (door) {
10670
12232
  const bb = door.mirrors.reduce(
10671
12233
  (acc, o) => {
@@ -10678,14 +12240,14 @@ var GameVehicles = (() => {
10678
12240
  },
10679
12241
  null
10680
12242
  );
10681
- 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])] };
10682
12244
  }
10683
12245
  add({
10684
12246
  id: `mirror_${side(s)}`,
10685
12247
  role: "mirror",
10686
12248
  name: t("mechanics.mirror", { side: t(`sides.${side(s)}`), sideM: t(`sides.${side(s)}M`) }),
10687
12249
  material: "plastic",
10688
- box,
12250
+ box: box2,
10689
12251
  mass: 1.1,
10690
12252
  hp: 1500,
10691
12253
  ignitionC: 400,
@@ -10875,13 +12437,13 @@ var GameVehicles = (() => {
10875
12437
  (parent ?? this.c.vehicle).add(m);
10876
12438
  return m;
10877
12439
  }
10878
- surface(axis, sign, B, samples) {
12440
+ surface(axis, sign2, B, samples) {
10879
12441
  const V3 = this.THREE.Vector3;
10880
12442
  const hits = [];
10881
12443
  const far = 3;
10882
12444
  for (const [u, v] of samples) {
10883
- 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);
10884
- 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);
10885
12447
  const h = this.I.probe(this.c, o, d, far);
10886
12448
  if (h) hits.push(axis === "z" ? h.p.z : h.p.x);
10887
12449
  }
@@ -11031,12 +12593,12 @@ var GameVehicles = (() => {
11031
12593
  const z = this.zone(p);
11032
12594
  if (!z) return false;
11033
12595
  const V3 = this.THREE.Vector3;
11034
- const list = z.glass.filter((g) => g.parent && g.visible);
11035
- if (!list.length) return false;
12596
+ const list2 = z.glass.filter((g) => g.parent && g.visible);
12597
+ if (!list2.length) return false;
11036
12598
  const pt = new V3(...point);
11037
- let best = list[0];
12599
+ let best = list2[0];
11038
12600
  let bd = Infinity;
11039
- for (const g of list) {
12601
+ for (const g of list2) {
11040
12602
  g.geometry.computeBoundingBox();
11041
12603
  const bb = g.geometry.boundingBox.clone().applyMatrix4(g.matrix);
11042
12604
  const d = bb.distanceToPoint(pt);
@@ -11337,6 +12899,9 @@ var GameVehicles = (() => {
11337
12899
  const headless = !!options.headless;
11338
12900
  const seedBase = strHash(`${c.key}|${id}|${options.seed ?? 0}`);
11339
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}`));
11340
12905
  let clock = 0;
11341
12906
  let model;
11342
12907
  let vis = null;
@@ -11404,7 +12969,7 @@ var GameVehicles = (() => {
11404
12969
  dents: [],
11405
12970
  holes: [],
11406
12971
  cracks: [],
11407
- liters: d.contains?.liters,
12972
+ liters: d.contains ? d.role === "fuel_tank" ? d.contains.liters * fuelFraction : d.contains.liters : void 0,
11408
12973
  lost: false,
11409
12974
  status: "intact",
11410
12975
  flat: false,
@@ -11422,6 +12987,7 @@ var GameVehicles = (() => {
11422
12987
  wasLit = true;
11423
12988
  deadEngine = false;
11424
12989
  rand = rng(seedBase);
12990
+ igniteRand = rng(strHash(`${c.key}|${id}|ignition|${options.seed ?? 0}`));
11425
12991
  steerSign = rand() < 0.5 ? -1 : 1;
11426
12992
  vis = headless ? null : new VehicleVisuals(kit, c, model);
11427
12993
  recomputeGroups();
@@ -11443,8 +13009,8 @@ var GameVehicles = (() => {
11443
13009
  comps.get(r).push(p);
11444
13010
  }
11445
13011
  const hubRoot = parent.has(model.hub) ? find(model.hub) : "";
11446
- const list = [...comps.entries()].sort((a, b) => a[0] === hubRoot ? -1 : b[0] === hubRoot ? 1 : 0);
11447
- 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);
11448
13014
  groupOf = /* @__PURE__ */ new Map();
11449
13015
  groupsCache.forEach((g, i) => g.forEach((p) => groupOf.set(p, i)));
11450
13016
  return groupsCache;
@@ -11716,8 +13282,19 @@ var GameVehicles = (() => {
11716
13282
  if (!p.burning && p.temp > AMBIENT) p.temp = Math.max(AMBIENT, p.temp - dt * 6);
11717
13283
  }
11718
13284
  for (const f of flames) heat(centreOf(f.def), 0.8, 500, dt * 0.5, out, 0);
13285
+ fireNearLeak(flames, dt, out);
11719
13286
  thermal(out);
11720
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
+ };
11721
13298
  const normalOf = (d, v) => faceNormal(d, v);
11722
13299
  const ballistic = (h, out) => {
11723
13300
  sync();
@@ -11785,10 +13362,21 @@ var GameVehicles = (() => {
11785
13362
  let amount = through ? E * 0.6 : E * 0.25;
11786
13363
  if (through && (d.role === "battery" || d.role === "alternator")) amount = Math.max(amount, p.hp * 0.65);
11787
13364
  damage(p, amount, "ballistic", h.source, out, worldPoint);
11788
- 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);
11789
13366
  }
11790
13367
  }
11791
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
+ };
11792
13380
  const blunt = (h, out) => {
11793
13381
  sync();
11794
13382
  const M = toFrameMatrix();
@@ -11852,7 +13440,7 @@ var GameVehicles = (() => {
11852
13440
  const verdict = kit.blastDamage(c, E / (1 + dist * dist * 4) / 50);
11853
13441
  if (verdict === "explosion") explode(tank, h.source, out);
11854
13442
  else if (verdict === "fire") {
11855
- tank.temp = Math.max(tank.temp, tank.def.ignitionC ?? 260);
13443
+ light(tank, out);
11856
13444
  startLeak(tank, centreOf(tank.def).setY(tank.def.box.center[1] - tank.def.box.size[1] / 2 + 0.05), 12, out, dir);
11857
13445
  }
11858
13446
  }
@@ -11866,9 +13454,9 @@ var GameVehicles = (() => {
11866
13454
  thermal(out);
11867
13455
  };
11868
13456
  const sound = (role) => {
11869
- const list = [...ps.values()].filter((p) => p.def.role === role);
11870
- if (!list.length) return true;
11871
- 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);
11872
13460
  };
11873
13461
  const tankPart = () => [...ps.values()].find((p) => p.def.role === "fuel_tank" && p.def.contains);
11874
13462
  const computeStatus = () => {
@@ -12060,8 +13648,8 @@ var GameVehicles = (() => {
12060
13648
  queue = [];
12061
13649
  const hood = c.hood;
12062
13650
  if (hood) {
12063
- const sign = hood.sign || 1;
12064
- const cur = hood.piv.rotation.z / sign;
13651
+ const sign2 = hood.sign || 1;
13652
+ const cur = hood.piv.rotation.z / sign2;
12065
13653
  if (Math.abs(cur - hood.ang) > 1e-4) {
12066
13654
  hood.ang = cur;
12067
13655
  hood.target = cur;