@adatechnology/game-vehicles 0.2.0 → 0.4.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) {
@@ -984,46 +2501,79 @@ var GameVehicles = (() => {
984
2501
  }
985
2502
  const p = geo.attributes.position;
986
2503
  const n = p.count;
987
- const FN = [];
988
- const map = {};
989
- const A = new V3();
990
- const B = new V3();
991
- const C = new V3();
992
- const e1 = new V3();
993
- const e2 = new V3();
2504
+ const faces = n / 3;
2505
+ const FN = new Float64Array(faces * 3);
2506
+ const map = /* @__PURE__ */ new Map();
2507
+ const RANGE = 65536;
994
2508
  function key(i) {
995
- return Math.round(p.getX(i) * 2e3) + "," + Math.round(p.getY(i) * 2e3) + "," + Math.round(p.getZ(i) * 2e3);
2509
+ const x = Math.round(p.getX(i) * 2e3);
2510
+ const y = Math.round(p.getY(i) * 2e3);
2511
+ const z = Math.round(p.getZ(i) * 2e3);
2512
+ if (Math.abs(x) < RANGE && Math.abs(y) < RANGE && Math.abs(z) < RANGE) return ((x + RANGE) * 131072 + (y + RANGE)) * 131072 + (z + RANGE);
2513
+ return x + "," + y + "," + z;
996
2514
  }
997
2515
  const keys = new Array(n);
998
- for (let f = 0; f < n / 3; f++) {
999
- A.fromBufferAttribute(p, 3 * f);
1000
- B.fromBufferAttribute(p, 3 * f + 1);
1001
- C.fromBufferAttribute(p, 3 * f + 2);
1002
- const nn = new V3().crossVectors(e1.subVectors(C, B), e2.subVectors(A, B));
1003
- if (nn.lengthSq() > 1e-20) nn.normalize();
1004
- FN.push(nn);
2516
+ for (let f = 0; f < faces; f++) {
2517
+ const ax = p.getX(3 * f);
2518
+ const ay = p.getY(3 * f);
2519
+ const az = p.getZ(3 * f);
2520
+ const bx = p.getX(3 * f + 1);
2521
+ const by = p.getY(3 * f + 1);
2522
+ const bz = p.getZ(3 * f + 1);
2523
+ const cx = p.getX(3 * f + 2);
2524
+ const cy = p.getY(3 * f + 2);
2525
+ const cz = p.getZ(3 * f + 2);
2526
+ const e1x = cx - bx;
2527
+ const e1y = cy - by;
2528
+ const e1z = cz - bz;
2529
+ const e2x = ax - bx;
2530
+ const e2y = ay - by;
2531
+ const e2z = az - bz;
2532
+ let nx = e1y * e2z - e1z * e2y;
2533
+ let ny = e1z * e2x - e1x * e2z;
2534
+ let nz = e1x * e2y - e1y * e2x;
2535
+ if (nx * nx + ny * ny + nz * nz > 1e-20) {
2536
+ const inv = 1 / (Math.sqrt(nx * nx + ny * ny + nz * nz) || 1);
2537
+ nx *= inv;
2538
+ ny *= inv;
2539
+ nz *= inv;
2540
+ }
2541
+ FN[3 * f] = nx;
2542
+ FN[3 * f + 1] = ny;
2543
+ FN[3 * f + 2] = nz;
1005
2544
  for (let k = 0; k < 3; k++) {
1006
2545
  const kk = key(3 * f + k);
1007
2546
  keys[3 * f + k] = kk;
1008
- (map[kk] || (map[kk] = [])).push(f);
2547
+ const list2 = map.get(kk);
2548
+ if (list2) list2.push(f);
2549
+ else map.set(kk, [f]);
1009
2550
  }
1010
2551
  }
1011
2552
  const cs = Math.cos(deg * PI / 180);
1012
2553
  const out = new Float32Array(n * 3);
1013
- const s = new V3();
1014
2554
  for (let i = 0; i < n; i++) {
1015
- const fi = i / 3 | 0;
1016
- const list = map[keys[i]];
1017
- s.set(0, 0, 0);
1018
- for (let j = 0; j < list.length; j++) {
1019
- const g = FN[list[j]];
1020
- if (g.dot(FN[fi]) >= cs) s.add(g);
1021
- }
1022
- if (s.lengthSq() < 1e-20) s.copy(FN[fi]);
1023
- s.normalize();
1024
- out[3 * i] = s.x;
1025
- out[3 * i + 1] = s.y;
1026
- out[3 * i + 2] = s.z;
2555
+ const fi = (i / 3 | 0) * 3;
2556
+ const list2 = map.get(keys[i]);
2557
+ let sx = 0;
2558
+ let sy = 0;
2559
+ let sz = 0;
2560
+ for (let j = 0; j < list2.length; j++) {
2561
+ const g = list2[j] * 3;
2562
+ if (FN[g] * FN[fi] + FN[g + 1] * FN[fi + 1] + FN[g + 2] * FN[fi + 2] >= cs) {
2563
+ sx += FN[g];
2564
+ sy += FN[g + 1];
2565
+ sz += FN[g + 2];
2566
+ }
2567
+ }
2568
+ if (sx * sx + sy * sy + sz * sz < 1e-20) {
2569
+ sx = FN[fi];
2570
+ sy = FN[fi + 1];
2571
+ sz = FN[fi + 2];
2572
+ }
2573
+ const inv = 1 / (Math.sqrt(sx * sx + sy * sy + sz * sz) || 1);
2574
+ out[3 * i] = sx * inv;
2575
+ out[3 * i + 1] = sy * inv;
2576
+ out[3 * i + 2] = sz * inv;
1027
2577
  }
1028
2578
  geo.setAttribute("normal", new THREE.BufferAttribute(out, 3));
1029
2579
  geo.userData.crease = true;
@@ -1082,7 +2632,7 @@ var GameVehicles = (() => {
1082
2632
  const pos = [];
1083
2633
  const col = [];
1084
2634
  const idx = [];
1085
- const ring = seg + 1;
2635
+ const ring2 = seg + 1;
1086
2636
  st.forEach(function(s) {
1087
2637
  for (let j = 0; j <= seg; j++) {
1088
2638
  const a = j / seg * PI * 2;
@@ -1100,8 +2650,8 @@ var GameVehicles = (() => {
1100
2650
  });
1101
2651
  for (let i = 0; i < st.length - 1; i++)
1102
2652
  for (let k = 0; k < seg; k++) {
1103
- const a0 = i * ring + k;
1104
- const b0 = a0 + ring;
2653
+ const a0 = i * ring2 + k;
2654
+ const b0 = a0 + ring2;
1105
2655
  idx.push(a0, a0 + 1, b0, a0 + 1, b0 + 1, b0);
1106
2656
  }
1107
2657
  ;
@@ -1115,7 +2665,7 @@ var GameVehicles = (() => {
1115
2665
  col.push(cc.r, cc.g, cc.b);
1116
2666
  }
1117
2667
  for (let k = 0; k < seg; k++) {
1118
- const a = si * ring + k;
2668
+ const a = si * ring2 + k;
1119
2669
  if (e === 0) idx.push(ci, a + 1, a);
1120
2670
  else idx.push(ci, a, a + 1);
1121
2671
  }
@@ -1253,13 +2803,13 @@ var GameVehicles = (() => {
1253
2803
  });
1254
2804
  tris = next;
1255
2805
  });
1256
- function build(list) {
2806
+ function build(list2) {
1257
2807
  const g = new THREE.BufferGeometry();
1258
2808
  let off = 0;
1259
2809
  names.forEach(function(nm, a) {
1260
- const arr = new Float32Array(list.length * 3 * sizes[a]);
2810
+ const arr = new Float32Array(list2.length * 3 * sizes[a]);
1261
2811
  let w = 0;
1262
- list.forEach(function(t) {
2812
+ list2.forEach(function(t) {
1263
2813
  for (let k = 0; k < 3; k++) for (let q = 0; q < sizes[a]; q++) arr[w++] = t[k][off + q];
1264
2814
  });
1265
2815
  g.setAttribute(nm, new THREE.BufferAttribute(arr, sizes[a]));
@@ -1274,10 +2824,10 @@ var GameVehicles = (() => {
1274
2824
  });
1275
2825
  return out;
1276
2826
  }
1277
- function mergeGeometries(list) {
2827
+ function mergeGeometries(list2) {
1278
2828
  const pos = [];
1279
2829
  const nor = [];
1280
- list.forEach(function(g2) {
2830
+ list2.forEach(function(g2) {
1281
2831
  const n = g2.index ? g2.toNonIndexed() : g2;
1282
2832
  if (!n.attributes.normal) n.computeVertexNormals();
1283
2833
  const pa = n.attributes.position.array;
@@ -1336,13 +2886,13 @@ var GameVehicles = (() => {
1336
2886
  };
1337
2887
  kit.holeSide = function(poly, o) {
1338
2888
  o = o || {};
1339
- let area = 0;
2889
+ let area3 = 0;
1340
2890
  for (let i = 0; i < poly.length; i++) {
1341
2891
  const a = poly[i];
1342
2892
  const b = poly[(i + 1) % poly.length];
1343
- area += a[0] * b[1] - b[0] * a[1];
2893
+ area3 += a[0] * b[1] - b[0] * a[1];
1344
2894
  }
1345
- const sg = area > 0 ? 1 : -1;
2895
+ const sg = area3 > 0 ? 1 : -1;
1346
2896
  const E2 = poly.map(function(a, i) {
1347
2897
  const b = poly[(i + 1) % poly.length];
1348
2898
  const dx = b[0] - a[0];
@@ -1645,11 +3195,11 @@ var GameVehicles = (() => {
1645
3195
  return d.clone().applyQuaternion(this.vehicle.quaternion);
1646
3196
  }
1647
3197
  /** 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) {
3198
+ zone(id, label, box2, parts, o) {
1649
3199
  const d = Object.assign({ id }, o || {});
1650
3200
  if (label === false) d.remove = true;
1651
3201
  else if (label) d.name = label;
1652
- if (box) d.box = I.normalizeBox(box);
3202
+ if (box2) d.box = I.normalizeBox(box2);
1653
3203
  if (parts) d.objs = Array.isArray(parts) ? parts.slice() : [parts];
1654
3204
  this.zoneDefs.push(d);
1655
3205
  return d;
@@ -1800,7 +3350,7 @@ var GameVehicles = (() => {
1800
3350
  const seg = o.seg || 24;
1801
3351
  const pos = [];
1802
3352
  const idx = [];
1803
- const ring = seg + 1;
3353
+ const ring2 = seg + 1;
1804
3354
  const axisY = o.axis === "y";
1805
3355
  st.forEach(function(s) {
1806
3356
  const a0 = s[1];
@@ -1822,8 +3372,8 @@ var GameVehicles = (() => {
1822
3372
  });
1823
3373
  for (let i = 0; i < st.length - 1; i++)
1824
3374
  for (let k = 0; k < seg; k++) {
1825
- const A = i * ring + k;
1826
- const B = A + ring;
3375
+ const A = i * ring2 + k;
3376
+ const B = A + ring2;
1827
3377
  if (axisY) idx.push(A, B, A + 1, A + 1, B, B + 1);
1828
3378
  else idx.push(A, A + 1, B, A + 1, B + 1, B);
1829
3379
  }
@@ -1836,7 +3386,7 @@ var GameVehicles = (() => {
1836
3386
  if (axisY) pos.push(cc, s[0], s[5] || 0);
1837
3387
  else pos.push(s[0], cc, s[5] || 0);
1838
3388
  for (let k = 0; k < seg; k++) {
1839
- const a = si * ring + k;
3389
+ const a = si * ring2 + k;
1840
3390
  const flip = e === 0 !== axisY;
1841
3391
  if (flip) idx.push(ci, a + 1, a);
1842
3392
  else idx.push(ci, a, a + 1);
@@ -2068,15 +3618,15 @@ var GameVehicles = (() => {
2068
3618
  c.part("glass", L, m, pos, o.reach || 0.9);
2069
3619
  return m;
2070
3620
  };
2071
- kit.asGlass = function(c, mesh, label, type, o) {
3621
+ kit.asGlass = function(c, mesh2, label, type, o) {
2072
3622
  o = o || {};
2073
- const i = c.deform.indexOf(mesh);
3623
+ const i = c.deform.indexOf(mesh2);
2074
3624
  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));
3625
+ if (mesh2.parent) mesh2.parent.remove(mesh2);
3626
+ const geo = mesh2.geometry;
3627
+ geo.applyMatrix4(new THREE.Matrix4().compose(mesh2.position, mesh2.quaternion, mesh2.scale));
2078
3628
  if (o.reuv !== false) o.reuv = true;
2079
- return kit.addGlass(c, geo, label, type, Object.assign({ mat: mesh.material }, o));
3629
+ return kit.addGlass(c, geo, label, type, Object.assign({ mat: mesh2.material }, o));
2080
3630
  };
2081
3631
  kit.sideWindows = function(c, polys, Wg, T, label, o) {
2082
3632
  o = o || {};
@@ -2122,12 +3672,12 @@ var GameVehicles = (() => {
2122
3672
  });
2123
3673
  });
2124
3674
  };
2125
- kit.cutMesh = function(mesh, tests, deg, o) {
2126
- const src = o && o.sub ? subdivide(mesh.geometry, o.sub) : mesh.geometry;
3675
+ kit.cutMesh = function(mesh2, tests, deg, o) {
3676
+ const src = o && o.sub ? subdivide(mesh2.geometry, o.sub) : mesh2.geometry;
2127
3677
  const r = cutGeometry(src, tests);
2128
3678
  crease(r.geo, deg || 42);
2129
- mesh.geometry.dispose();
2130
- mesh.geometry = r.geo;
3679
+ mesh2.geometry.dispose();
3680
+ mesh2.geometry = r.geo;
2131
3681
  return r.cut;
2132
3682
  };
2133
3683
  kit.quadDef = function(pb, pt, Wg, T, which, bev, o) {
@@ -2327,8 +3877,8 @@ var GameVehicles = (() => {
2327
3877
  const pz = nR === 1 ? 0 : (i ? 1 : -1) * w / 4;
2328
3878
  const back = gm(g, new THREE.CylinderGeometry(r * 1.06, r * 1.06, d * 0.8, 22), MATERIALS.housing, 0, 0, pz);
2329
3879
  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;
3880
+ const ring2 = gm(g, new THREE.TorusGeometry(r * 1.02, 0.013, 8, 30), MATERIALS.chrome, d / 2 - 6e-3, 0, pz);
3881
+ ring2.rotation.y = PI / 2;
2332
3882
  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
3883
  bowl.rotation.z = PI / 2;
2334
3884
  bowl.scale.y = 0.55;
@@ -2405,8 +3955,8 @@ var GameVehicles = (() => {
2405
3955
  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
3956
  e.rotation.z = PI / 2;
2407
3957
  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;
3958
+ const ring2 = gm(g, new THREE.TorusGeometry(r, 0.01, 6, 24), MATERIALS.chrome, d / 2 - 4e-3, 0, pz);
3959
+ ring2.rotation.y = PI / 2;
2410
3960
  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
3961
  ln.rotation.z = -PI / 2;
2412
3962
  ln.scale.y = 0.25;
@@ -2499,7 +4049,7 @@ var GameVehicles = (() => {
2499
4049
  } else if (st === "mesh") {
2500
4050
  gm(g, roundedBoxGeometry(0.05, h, w, Math.min(0.03, h * 0.3)), MATERIALS.housing);
2501
4051
  const cell = 0.026;
2502
- const list = [];
4052
+ const list2 = [];
2503
4053
  const rows = Math.floor(h * 0.85 / (cell * 1.6));
2504
4054
  const cols = Math.floor(w * 0.9 / (cell * 1.8));
2505
4055
  for (let rI = 0; rI < rows; rI++)
@@ -2508,9 +4058,9 @@ var GameVehicles = (() => {
2508
4058
  hg.rotateZ(PI / 2);
2509
4059
  hg.rotateX(PI / 6);
2510
4060
  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);
4061
+ list2.push(hg);
2512
4062
  }
2513
- if (list.length) gm(g, mergeGeometries(list), MATERIALS.trim);
4063
+ if (list2.length) gm(g, mergeGeometries(list2), MATERIALS.trim);
2514
4064
  gm(g, roundedBoxGeometry(0.022, 0.014, w, 6e-3), MATERIALS.chrome, 0.03, h / 2 - 8e-3, 0);
2515
4065
  } else if (st === "big") {
2516
4066
  const fr = roundedBoxGeometry(0.06, h, w, 0.03);
@@ -3351,7 +4901,7 @@ var GameVehicles = (() => {
3351
4901
  m.underbodyGroup = ug;
3352
4902
  const DK = makeMaterial("#1a1b1c", { roughness: 0.75, metalness: 0.3 });
3353
4903
  const ST = MATERIALS.steel;
3354
- function box(w, h, d, mat, x, y, z) {
4904
+ function box2(w, h, d, mat, x, y, z) {
3355
4905
  return c.roundBox(w, h, d, Math.min(0.02, h * 0.4), mat, x, y, z, { parent: ug });
3356
4906
  }
3357
4907
  function rod(a, b, r, mat) {
@@ -3368,9 +4918,9 @@ var GameVehicles = (() => {
3368
4918
  rod(new V3(rx, rr, -zW), new V3(rx, rr, zW), 0.045, ST);
3369
4919
  c.sphere(0.13, ST, rx, rr, 0, 1, 0.85, 1, { parent: ug });
3370
4920
  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);
4921
+ } else box2(0.09, 0.08, zW * 2, DK, rx + 0.08, Math.max(yU, rr - 0.12), 0);
3372
4922
  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);
4923
+ box2(Math.min(0.5, (fx - rx) * 0.2), 0.1, 0.38, DK, fx - 0.25, yU + 0.01, -0.05);
3374
4924
  const tz = W * 0.3;
3375
4925
  const mx = rx - 0.15;
3376
4926
  const ty = Math.max(0.14, by - 0.06);
@@ -3387,10 +4937,10 @@ var GameVehicles = (() => {
3387
4937
  { parent: ug, seg: 30, rs: 6 }
3388
4938
  );
3389
4939
  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);
4940
+ box2(0.24, 0.06, 0.2, DK, (fx + rx) / 2 + 0.1, ty, 0.16);
4941
+ 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
4942
  } else {
3393
- box(
4943
+ box2(
3394
4944
  (fx - rx) * 0.72,
3395
4945
  0.1,
3396
4946
  W - 0.4,
@@ -3399,7 +4949,7 @@ var GameVehicles = (() => {
3399
4949
  Math.max(0.14, by - 0.03),
3400
4950
  0
3401
4951
  );
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);
4952
+ 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
4953
  }
3404
4954
  if (o.spare && m.rear < rx - 0.7) {
3405
4955
  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 +5277,16 @@ var GameVehicles = (() => {
3727
5277
  }
3728
5278
  } else {
3729
5279
  const ns = o.spokes || 28;
3730
- const list = [];
5280
+ const list2 = [];
3731
5281
  for (let jj = 0; jj < ns; jj++) {
3732
5282
  const a2 = jj / ns * PI * 2;
3733
5283
  const cg = new THREE.CylinderGeometry(25e-4, 25e-4, sl, 4);
3734
5284
  cg.applyMatrix4(
3735
5285
  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
5286
  );
3737
- list.push(cg);
5287
+ list2.push(cg);
3738
5288
  }
3739
- g.add(new THREE.Mesh(mergeGeometries(list), MATERIALS.chrome));
5289
+ g.add(new THREE.Mesh(mergeGeometries(list2), MATERIALS.chrome));
3740
5290
  }
3741
5291
  const hub = new THREE.Mesh(new THREE.CylinderGeometry(0.035, 0.035, 0.1, 12), MATERIALS.steel);
3742
5292
  hub.rotation.x = PI / 2;
@@ -3918,9 +5468,9 @@ var GameVehicles = (() => {
3918
5468
  return puffTexture(k * 17, true);
3919
5469
  });
3920
5470
  }
3921
- const list = fire ? FIRE_TEXTURES : SMOKE_TEXTURES;
5471
+ const list2 = fire ? FIRE_TEXTURES : SMOKE_TEXTURES;
3922
5472
  const mat = new THREE.SpriteMaterial({
3923
- map: list[Math.floor(Math.random() * list.length)],
5473
+ map: list2[Math.floor(Math.random() * list2.length)],
3924
5474
  color: new THREE.Color(col),
3925
5475
  transparent: true,
3926
5476
  depthWrite: false,
@@ -5229,7 +6779,7 @@ var GameVehicles = (() => {
5229
6779
  }
5230
6780
  });
5231
6781
  const dset = new Set(c.deform);
5232
- const box = new THREE.Box3();
6782
+ const box2 = new THREE.Box3();
5233
6783
  c.vehicle.children.forEach(function(o) {
5234
6784
  if (dset.has(o) || o.userData.noFollow) return;
5235
6785
  if (!(o.isMesh || o.isGroup && o.userData.rigid)) return;
@@ -5240,10 +6790,10 @@ var GameVehicles = (() => {
5240
6790
  if (!o.geometry.boundingBox) o.geometry.computeBoundingBox();
5241
6791
  o.userData.bb0 = o.geometry.boundingBox.clone().applyMatrix4(o.matrix);
5242
6792
  } else {
5243
- box.setFromObject(o);
5244
- box.applyMatrix4(gInv);
5245
- o.userData.c0 = box.getCenter(new V3());
5246
- o.userData.bb0 = box.clone();
6793
+ box2.setFromObject(o);
6794
+ box2.applyMatrix4(gInv);
6795
+ o.userData.c0 = box2.getCenter(new V3());
6796
+ o.userData.bb0 = box2.clone();
5247
6797
  }
5248
6798
  o.userData.pos0 = o.position.clone();
5249
6799
  }
@@ -5539,11 +7089,11 @@ var GameVehicles = (() => {
5539
7089
  [12, "#d8322a", kit.t("blast.lethal", { m: 12 })],
5540
7090
  [30, "#e0a548", kit.t("blast.injury", { m: 30 })]
5541
7091
  ].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);
7092
+ const ring2 = new THREE.Mesh(new THREE.RingGeometry(r[0] - 0.25, r[0], 96), boomMat(r[1], 0.85));
7093
+ ring2.rotation.x = -PI / 2;
7094
+ ring2.position.y = 0.06;
7095
+ ring2.renderOrder = 5;
7096
+ g.add(ring2);
5547
7097
  const fill = new THREE.Mesh(new THREE.CircleGeometry(r[0], 96), boomMat(r[1], 0.07));
5548
7098
  fill.rotation.x = -PI / 2;
5549
7099
  fill.position.y = 0.05;
@@ -5560,7 +7110,7 @@ var GameVehicles = (() => {
5560
7110
  lb.rotation.x = -PI / 2;
5561
7111
  lb.position.set(0, 0.08, r[0] + r[0] * 0.06);
5562
7112
  g.add(lb);
5563
- c.bigFx.rings.push(ring, fill, lb);
7113
+ c.bigFx.rings.push(ring2, fill, lb);
5564
7114
  });
5565
7115
  }
5566
7116
  function detonate(c) {
@@ -5835,7 +7385,7 @@ var GameVehicles = (() => {
5835
7385
  const I = kit._;
5836
7386
  const { MATERIALS } = I;
5837
7387
  function rollMeshes(c) {
5838
- const list = [];
7388
+ const list2 = [];
5839
7389
  const thin = [];
5840
7390
  c.root.updateMatrixWorld(true);
5841
7391
  const inv = new THREE.Matrix4().copy(c.vehicle.matrixWorld).invert();
@@ -5853,9 +7403,9 @@ var GameVehicles = (() => {
5853
7403
  thin.push({ o: ob, M });
5854
7404
  return;
5855
7405
  }
5856
- list.push({ o: ob, M });
7406
+ list2.push({ o: ob, M });
5857
7407
  });
5858
- return { list, thin };
7408
+ return { list: list2, thin };
5859
7409
  }
5860
7410
  function convexHull(pts) {
5861
7411
  pts.sort(function(a, b) {
@@ -5938,7 +7488,7 @@ var GameVehicles = (() => {
5938
7488
  };
5939
7489
  function rollContact(c, A, q) {
5940
7490
  const m = c.meta;
5941
- const list = [];
7491
+ const list2 = [];
5942
7492
  const dy = A.cen[0] - q[0];
5943
7493
  const dz = A.cen[1] - q[1];
5944
7494
  const dl = Math.hypot(dy, dz) || 1;
@@ -5946,17 +7496,17 @@ var GameVehicles = (() => {
5946
7496
  const f = 0.75;
5947
7497
  const bike = m.kind === "bike";
5948
7498
  (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]);
7499
+ list2.push([new V3(A.x + k * A.L, q[0], q[1]), dir]);
5950
7500
  });
5951
7501
  if (!bike && Math.abs(dir.z) > 0.75 && m.top - m.belt > 0.25 && A.hits < 3) {
5952
7502
  const sd = -Math.sign(dir.z);
5953
7503
  const yw = m.belt + (m.top - m.belt) * 0.45;
5954
7504
  [-0.25, 0.2].forEach(function(k) {
5955
7505
  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)]);
7506
+ if (h) list2.push([h.p, new V3(0, 0, -sd)]);
5957
7507
  });
5958
7508
  }
5959
- list.forEach(function(e) {
7509
+ list2.forEach(function(e) {
5960
7510
  I.impact(c, e[0], e[1], f, { noStain: true, maxDepth: 0.1 });
5961
7511
  const pf = c.toEffects(e[0]);
5962
7512
  pf.y = Math.max(0.03, pf.y);
@@ -5966,15 +7516,15 @@ var GameVehicles = (() => {
5966
7516
  });
5967
7517
  A.hits++;
5968
7518
  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 });
7519
+ if (m.kind === "boat") I.burst(c, "water", c.toEffects(list2[0][0]).setY(0.05), 30, { speed: 3 });
5970
7520
  }
5971
- function exactMinY(c, list) {
7521
+ function exactMinY(c, list2) {
5972
7522
  c.root.updateMatrixWorld(true);
5973
7523
  const inv = new THREE.Matrix4().copy(c.root.matrixWorld).invert();
5974
7524
  const M = new THREE.Matrix4();
5975
7525
  const v = new V3();
5976
7526
  let mn = 1e9;
5977
- list.forEach(function(e) {
7527
+ list2.forEach(function(e) {
5978
7528
  const ob = e.o;
5979
7529
  if (!ob.parent || !I.shown(c, ob)) return;
5980
7530
  M.multiplyMatrices(inv, ob.matrixWorld);
@@ -6681,6 +8231,153 @@ var GameVehicles = (() => {
6681
8231
  return hole;
6682
8232
  }
6683
8233
  const _zr = new THREE.Raycaster();
8234
+ const PROXY_MIN_TRIANGLES = 500;
8235
+ const STROKE_REACH = 0.6;
8236
+ const _pv = new V3();
8237
+ const _pm = new THREE.Matrix4();
8238
+ function decalRegion(c, z) {
8239
+ const B = z.box;
8240
+ const lo = [0, 1, 2].map(function(k) {
8241
+ return I.boxLo(B, k) - STROKE_REACH;
8242
+ });
8243
+ const hi = [0, 1, 2].map(function(k) {
8244
+ return I.boxHi(B, k) + STROKE_REACH;
8245
+ });
8246
+ const a = axisOf(z.dir);
8247
+ const dn = z.dir.clone().normalize();
8248
+ const sg = z.dir.getComponent(a) > 0 ? 1 : -1;
8249
+ const outer = sg > 0 ? I.boxLo(B, a) - 0.6 : I.boxHi(B, a) + 0.6;
8250
+ const far = 0.6 + (I.boxHi(B, a) - I.boxLo(B, a)) + 0.05;
8251
+ for (let i = 0; i < 8; i++) {
8252
+ const p = new V3();
8253
+ p.setComponent(a, outer);
8254
+ [0, 1, 2].filter(function(k) {
8255
+ return k !== a;
8256
+ }).forEach(function(k, j) {
8257
+ p.setComponent(k, i >> j & 1 ? I.boxHi(B, k) : I.boxLo(B, k));
8258
+ });
8259
+ for (let e = 0; e < 2; e++) {
8260
+ const q = e ? p.clone().addScaledVector(dn, far) : p;
8261
+ for (let k = 0; k < 3; k++) {
8262
+ lo[k] = Math.min(lo[k], q.getComponent(k) - 0.05);
8263
+ hi[k] = Math.max(hi[k], q.getComponent(k) + 0.05);
8264
+ }
8265
+ }
8266
+ }
8267
+ return { lo, hi };
8268
+ }
8269
+ function regionCands(c, z, cands) {
8270
+ if (!cands.some(heavy)) return cands;
8271
+ const R = decalRegion(c, z);
8272
+ const Mw = c.vehicle.matrixWorld;
8273
+ return cands.map(function(o) {
8274
+ if (!heavy(o)) return o;
8275
+ if (!o.geometry.boundingSphere) o.geometry.computeBoundingSphere();
8276
+ if (!o.geometry.boundingBox) o.geometry.computeBoundingBox();
8277
+ return proxyIn(o, Mw, R);
8278
+ });
8279
+ }
8280
+ function heavy(o) {
8281
+ const g = o.geometry;
8282
+ const pos = g && g.attributes && g.attributes.position;
8283
+ return o.raycast === THREE.Mesh.prototype.raycast && !o.isSkinnedMesh && !Array.isArray(o.material) && pos && !pos.isInterleavedBufferAttribute && pos.itemSize === 3 && !g.groups.length && !(g.morphAttributes && g.morphAttributes.position) && (g.index ? g.index.count : pos.count) / 3 >= PROXY_MIN_TRIANGLES;
8284
+ }
8285
+ function proxyIn(o, Mw, R) {
8286
+ _pm.copy(o.matrixWorld).invert().multiply(Mw);
8287
+ const lo = [Infinity, Infinity, Infinity];
8288
+ const hi = [-Infinity, -Infinity, -Infinity];
8289
+ for (let i = 0; i < 8; i++) {
8290
+ _pv.set(i & 1 ? R.hi[0] : R.lo[0], i & 2 ? R.hi[1] : R.lo[1], i & 4 ? R.hi[2] : R.lo[2]).applyMatrix4(_pm);
8291
+ for (let k = 0; k < 3; k++) {
8292
+ lo[k] = Math.min(lo[k], _pv.getComponent(k));
8293
+ hi[k] = Math.max(hi[k], _pv.getComponent(k));
8294
+ }
8295
+ }
8296
+ const g = o.geometry;
8297
+ const P = g.attributes.position.array;
8298
+ const ix = g.index ? g.index.array : null;
8299
+ const n = ix ? ix.length : P.length / 3;
8300
+ const keep = new Uint32Array(n - n % 3);
8301
+ let nk = 0;
8302
+ for (let t = 0; t + 2 < n; t += 3) {
8303
+ const a = (ix ? ix[t] : t) * 3;
8304
+ const b = (ix ? ix[t + 1] : t + 1) * 3;
8305
+ const d = (ix ? ix[t + 2] : t + 2) * 3;
8306
+ let out = false;
8307
+ for (let k = 0; k < 3 && !out; k++) {
8308
+ const u = P[a + k];
8309
+ const v = P[b + k];
8310
+ const w = P[d + k];
8311
+ out = Math.max(u, v, w) < lo[k] || Math.min(u, v, w) > hi[k];
8312
+ }
8313
+ if (!out) {
8314
+ keep[nk++] = ix ? ix[t] : t;
8315
+ keep[nk++] = ix ? ix[t + 1] : t + 1;
8316
+ keep[nk++] = ix ? ix[t + 2] : t + 2;
8317
+ }
8318
+ }
8319
+ if (!nk) return null;
8320
+ const kept = keep.slice(0, nk);
8321
+ const box2 = new Float32Array(kept.length / 3 * 6);
8322
+ for (let t = 0, j = 0; t < kept.length; t += 3, j += 6) {
8323
+ const a = kept[t] * 3;
8324
+ const b = kept[t + 1] * 3;
8325
+ const d = kept[t + 2] * 3;
8326
+ for (let k = 0; k < 3; k++) {
8327
+ box2[j + k] = Math.min(P[a + k], P[b + k], P[d + k]);
8328
+ box2[j + 3 + k] = Math.max(P[a + k], P[b + k], P[d + k]);
8329
+ }
8330
+ }
8331
+ const pg = new THREE.BufferGeometry();
8332
+ pg.setAttribute("position", g.attributes.position);
8333
+ pg.setIndex(new THREE.BufferAttribute(new Uint32Array(kept.length), 1));
8334
+ pg.boundingSphere = g.boundingSphere;
8335
+ pg.boundingBox = g.boundingBox;
8336
+ const proxy = new THREE.Mesh(pg, o.material);
8337
+ proxy.layers.mask = o.layers.mask;
8338
+ proxy.proxyOf = o;
8339
+ proxy.kept = kept;
8340
+ proxy.keptBox = box2;
8341
+ return proxy;
8342
+ }
8343
+ function narrowTo(rc, proxy) {
8344
+ const o = proxy.proxyOf;
8345
+ const ray = rc.ray;
8346
+ _pm.copy(o.matrixWorld).invert();
8347
+ _pv.copy(ray.origin).applyMatrix4(_pm);
8348
+ const x0 = _pv.x;
8349
+ const y0 = _pv.y;
8350
+ const z0 = _pv.z;
8351
+ _pv.copy(ray.direction).multiplyScalar(rc.far).add(ray.origin).applyMatrix4(_pm);
8352
+ const lo0 = Math.min(x0, _pv.x) - 1e-4;
8353
+ const lo1 = Math.min(y0, _pv.y) - 1e-4;
8354
+ const lo2 = Math.min(z0, _pv.z) - 1e-4;
8355
+ const hi0 = Math.max(x0, _pv.x) + 1e-4;
8356
+ const hi1 = Math.max(y0, _pv.y) + 1e-4;
8357
+ const hi2 = Math.max(z0, _pv.z) + 1e-4;
8358
+ const K = proxy.kept;
8359
+ const X = proxy.keptBox;
8360
+ const dst = proxy.geometry.index.array;
8361
+ let m = 0;
8362
+ for (let t = 0, j = 0; t < K.length; t += 3, j += 6) {
8363
+ if (X[j + 3] < lo0 || X[j] > hi0 || X[j + 4] < lo1 || X[j + 1] > hi1 || X[j + 5] < lo2 || X[j + 2] > hi2) continue;
8364
+ dst[m++] = K[t];
8365
+ dst[m++] = K[t + 1];
8366
+ dst[m++] = K[t + 2];
8367
+ }
8368
+ proxy.geometry.setDrawRange(0, m);
8369
+ }
8370
+ function castAt(rc, cands) {
8371
+ for (let i = 0; i < cands.length; i++) {
8372
+ const q = cands[i];
8373
+ if (!q.proxyOf) continue;
8374
+ q.matrixWorld.copy(q.proxyOf.matrixWorld);
8375
+ narrowTo(rc, q);
8376
+ }
8377
+ const hits = rc.intersectObjects(cands, false);
8378
+ for (let i = 0; i < hits.length; i++) if (hits[i].object.proxyOf) hits[i].object = hits[i].object.proxyOf;
8379
+ return hits;
8380
+ }
6684
8381
  function zoneCands(c, z, inv, glassToo) {
6685
8382
  const out = [];
6686
8383
  c.vehicle.traverse(function(o) {
@@ -6733,7 +8430,7 @@ var GameVehicles = (() => {
6733
8430
  });
6734
8431
  _zr.set(org.clone().applyMatrix4(Mw), d.clone().transformDirection(Mw));
6735
8432
  _zr.far = 0.6 + (I.boxHi(B, a) - I.boxLo(B, a)) + 0.05;
6736
- const hits = _zr.intersectObjects(cands, false);
8433
+ const hits = castAt(_zr, cands);
6737
8434
  for (let i = 0; i < hits.length; i++) {
6738
8435
  const h = hits[i];
6739
8436
  if (!h.face) continue;
@@ -6761,7 +8458,7 @@ var GameVehicles = (() => {
6761
8458
  const q = s.p.clone().addScaledVector(t, len * 0.5 * k / M);
6762
8459
  _rt.set(q.clone().addScaledVector(n, 0.08).applyMatrix4(Mw), n.clone().negate().transformDirection(Mw));
6763
8460
  _rt.far = 0.2;
6764
- const h = _rt.intersectObjects(cands, false).find(function(x) {
8461
+ const h = castAt(_rt, cands).find(function(x) {
6765
8462
  return x.face;
6766
8463
  });
6767
8464
  if (!h) return null;
@@ -6832,6 +8529,7 @@ var GameVehicles = (() => {
6832
8529
  const R = c.rand;
6833
8530
  const need = (spec.fine || 0) + (spec.deep || 0) + (spec.chip || 0) + (spec.rust || 0) + (spec.tear || 0) + (spec.ding || 0) + (spec.soot || 0) + (spec.crater || 0);
6834
8531
  if (!need) return [];
8532
+ cands = regionCands(c, z, cands).filter(Boolean);
6835
8533
  const S = zoneSurf(c, z, need + 2, R, cands);
6836
8534
  let k = 0;
6837
8535
  const ang0 = R() * PI;
@@ -6912,8 +8610,25 @@ var GameVehicles = (() => {
6912
8610
  }
6913
8611
  function subdivIn(geo, rel, B, maxLen) {
6914
8612
  const a = geo.attributes.position.array;
6915
- const out = [];
6916
- const v = new V3();
8613
+ let out = new Float32Array(a.length + 4096);
8614
+ let used = 0;
8615
+ const push = function(a0, a1, a2, b0, b1, b2, c0, c1, c2) {
8616
+ if (used + 9 > out.length) {
8617
+ const bigger = new Float32Array(out.length * 2);
8618
+ bigger.set(out);
8619
+ out = bigger;
8620
+ }
8621
+ out[used] = a0;
8622
+ out[used + 1] = a1;
8623
+ out[used + 2] = a2;
8624
+ out[used + 3] = b0;
8625
+ out[used + 4] = b1;
8626
+ out[used + 5] = b2;
8627
+ out[used + 6] = c0;
8628
+ out[used + 7] = c1;
8629
+ out[used + 8] = c2;
8630
+ used += 9;
8631
+ };
6917
8632
  const e = 0.15;
6918
8633
  function d(p, q) {
6919
8634
  return Math.hypot(p[0] - q[0], p[1] - q[1], p[2] - q[2]);
@@ -6927,7 +8642,7 @@ var GameVehicles = (() => {
6927
8642
  const ca = d(C, A);
6928
8643
  const mx = Math.max(ab, bc, ca);
6929
8644
  if (mx <= maxLen || depth > 9) {
6930
- out.push(A[0], A[1], A[2], Bv[0], Bv[1], Bv[2], C[0], C[1], C[2]);
8645
+ push(A[0], A[1], A[2], Bv[0], Bv[1], Bv[2], C[0], C[1], C[2]);
6931
8646
  return;
6932
8647
  }
6933
8648
  if (mx === ab) {
@@ -6944,31 +8659,26 @@ var GameVehicles = (() => {
6944
8659
  tri(F2, Bv, C, depth + 1);
6945
8660
  }
6946
8661
  }
8662
+ const M = rel.elements;
8663
+ const at = function(x, y, z, k) {
8664
+ const w = 1 / (M[3] * x + M[7] * y + M[11] * z + M[15]);
8665
+ return k === 0 ? (M[0] * x + M[4] * y + M[8] * z + M[12]) * w : k === 1 ? (M[1] * x + M[5] * y + M[9] * z + M[13]) * w : (M[2] * x + M[6] * y + M[10] * z + M[14]) * w;
8666
+ };
8667
+ const lo = [B.x0 - e, B.y0 - e, B.z0 - e];
8668
+ const hi = [B.x1 + e, B.y1 + e, B.z1 + e];
6947
8669
  for (let i = 0; i < a.length; i += 9) {
6948
- const A = [a[i], a[i + 1], a[i + 2]];
6949
- const Bv = [a[i + 3], a[i + 4], a[i + 5]];
6950
- const C = [a[i + 6], a[i + 7], a[i + 8]];
6951
- let x0 = 1e9;
6952
- let x1 = -1e9;
6953
- let y0 = 1e9;
6954
- let y1 = -1e9;
6955
- let z0 = 1e9;
6956
- let z1 = -1e9;
6957
- [A, Bv, C].forEach(function(p) {
6958
- v.set(p[0], p[1], p[2]).applyMatrix4(rel);
6959
- x0 = Math.min(x0, v.x);
6960
- x1 = Math.max(x1, v.x);
6961
- y0 = Math.min(y0, v.y);
6962
- y1 = Math.max(y1, v.y);
6963
- z0 = Math.min(z0, v.z);
6964
- z1 = Math.max(z1, v.z);
6965
- });
6966
- if (x1 < B.x0 - e || x0 > B.x1 + e || y1 < B.y0 - e || y0 > B.y1 + e || z1 < B.z0 - e || z0 > B.z1 + e)
6967
- out.push(A[0], A[1], A[2], Bv[0], Bv[1], Bv[2], C[0], C[1], C[2]);
6968
- else tri(A, Bv, C, 0);
8670
+ let outside = false;
8671
+ for (let k = 0; k < 3 && !outside; k++) {
8672
+ const p = at(a[i], a[i + 1], a[i + 2], k);
8673
+ const q = at(a[i + 3], a[i + 4], a[i + 5], k);
8674
+ const r = at(a[i + 6], a[i + 7], a[i + 8], k);
8675
+ outside = Math.max(p, q, r) < lo[k] || Math.min(p, q, r) > hi[k];
8676
+ }
8677
+ if (outside) push(a[i], a[i + 1], a[i + 2], a[i + 3], a[i + 4], a[i + 5], a[i + 6], a[i + 7], a[i + 8]);
8678
+ else tri([a[i], a[i + 1], a[i + 2]], [a[i + 3], a[i + 4], a[i + 5]], [a[i + 6], a[i + 7], a[i + 8]], 0);
6969
8679
  }
6970
8680
  const g = new THREE.BufferGeometry();
6971
- g.setAttribute("position", new THREE.Float32BufferAttribute(out, 3));
8681
+ g.setAttribute("position", new THREE.BufferAttribute(out.slice(0, used), 3));
6972
8682
  g.userData.crease = true;
6973
8683
  g.userData.p0 = null;
6974
8684
  return g;
@@ -6991,6 +8701,17 @@ var GameVehicles = (() => {
6991
8701
  });
6992
8702
  return out;
6993
8703
  }
8704
+ function boxInFrame(B, m, tmp) {
8705
+ const r = [Infinity, Infinity, Infinity, -Infinity, -Infinity, -Infinity];
8706
+ for (let i = 0; i < 8; i++) {
8707
+ tmp.set(i & 1 ? B.x1 : B.x0, i & 2 ? B.y1 : B.y0, i & 4 ? B.z1 : B.z0).applyMatrix4(m);
8708
+ for (let k = 0; k < 3; k++) {
8709
+ r[k] = Math.min(r[k], tmp.getComponent(k) - 1e-4);
8710
+ r[k + 3] = Math.max(r[k + 3], tmp.getComponent(k) + 1e-4);
8711
+ }
8712
+ }
8713
+ return r;
8714
+ }
6994
8715
  function zoneDent(c, z, dents, dirty, opt) {
6995
8716
  opt = opt || {};
6996
8717
  const B = z.box;
@@ -7045,6 +8766,7 @@ var GameVehicles = (() => {
7045
8766
  g = msh.geometry = g.clone();
7046
8767
  msh.userData.zCloned = true;
7047
8768
  }
8769
+ let boundsDue = false;
7048
8770
  const nonIdx = !g.index;
7049
8771
  const onlyPos = Object.keys(g.attributes).every(function(k) {
7050
8772
  return k === "position" || k === "normal";
@@ -7053,15 +8775,17 @@ var GameVehicles = (() => {
7053
8775
  const ng = subdivIn(g, rel, B, opt.sub || 0.075);
7054
8776
  g.dispose();
7055
8777
  g = msh.geometry = ng;
7056
- ng.computeBoundingSphere();
7057
- ng.computeBoundingBox();
8778
+ boundsDue = true;
7058
8779
  dirty.add(msh);
7059
8780
  }
7060
8781
  irel.copy(rel).invert();
7061
8782
  const pos = g.attributes.position;
7062
8783
  let changed = false;
8784
+ const near = boxInFrame(B, irel, v);
7063
8785
  for (let i = 0; i < pos.count; i++) {
7064
- v.fromBufferAttribute(pos, i).applyMatrix4(rel);
8786
+ v.fromBufferAttribute(pos, i);
8787
+ if (v.x < near[0] || v.x > near[3] || v.y < near[1] || v.y > near[4] || v.z < near[2] || v.z > near[5]) continue;
8788
+ v.applyMatrix4(rel);
7065
8789
  const f = field(v);
7066
8790
  if (f <= 1e-5) continue;
7067
8791
  v.addScaledVector(d, f);
@@ -7082,9 +8806,11 @@ var GameVehicles = (() => {
7082
8806
  }
7083
8807
  if (changed) {
7084
8808
  pos.needsUpdate = true;
8809
+ dirty.add(msh);
8810
+ }
8811
+ if (changed || boundsDue) {
7085
8812
  g.computeBoundingSphere();
7086
8813
  g.computeBoundingBox();
7087
- dirty.add(msh);
7088
8814
  }
7089
8815
  });
7090
8816
  const dset = new Set(c.deform);
@@ -8170,9 +9896,9 @@ var GameVehicles = (() => {
8170
9896
  down: new V3(0, 1, 0)
8171
9897
  }[d] || null;
8172
9898
  }
8173
- function resolveObjs(c, list, z) {
9899
+ function resolveObjs(c, list2, z) {
8174
9900
  const out = [];
8175
- (list || []).forEach(function(o) {
9901
+ (list2 || []).forEach(function(o) {
8176
9902
  if (!o) return;
8177
9903
  if (typeof o === "string") {
8178
9904
  c.parts.forEach(function(p) {
@@ -8200,22 +9926,22 @@ var GameVehicles = (() => {
8200
9926
  }
8201
9927
  function deriveZones(c) {
8202
9928
  const m = c.meta;
8203
- let list = [];
9929
+ let list2 = [];
8204
9930
  const byId = {};
8205
9931
  (m.zones === false ? [] : I.autoZones(c)).forEach(function(z) {
8206
9932
  byId[z.id] = z;
8207
- list.push(z);
9933
+ list2.push(z);
8208
9934
  });
8209
9935
  (c.zoneDefs || []).forEach(function(d) {
8210
9936
  let z = byId[d.id];
8211
9937
  if (d.remove) {
8212
9938
  if (z) {
8213
- list.splice(list.indexOf(z), 1);
9939
+ list2.splice(list2.indexOf(z), 1);
8214
9940
  delete byId[d.id];
8215
9941
  }
8216
- list.slice().forEach(function(q) {
9942
+ list2.slice().forEach(function(q) {
8217
9943
  if (q.id.indexOf(d.id + "_") === 0 && /^\d+$/.test(q.id.slice(d.id.length + 1))) {
8218
- list.splice(list.indexOf(q), 1);
9944
+ list2.splice(list2.indexOf(q), 1);
8219
9945
  delete byId[q.id];
8220
9946
  }
8221
9947
  });
@@ -8228,7 +9954,7 @@ var GameVehicles = (() => {
8228
9954
  } else {
8229
9955
  z = Object.assign({ type: "panel" }, d);
8230
9956
  byId[d.id] = z;
8231
- list.push(z);
9957
+ list2.push(z);
8232
9958
  }
8233
9959
  });
8234
9960
  const inv = I.worldInverseOf(c);
@@ -8261,10 +9987,10 @@ var GameVehicles = (() => {
8261
9987
  }
8262
9988
  z.box = I.normalizeBox(bb);
8263
9989
  });
8264
- list = list.filter(function(z) {
9990
+ list2 = list2.filter(function(z) {
8265
9991
  return z.box && z.name;
8266
9992
  });
8267
- list.forEach(function(z) {
9993
+ list2.forEach(function(z) {
8268
9994
  z.glass = (z.glass || []).slice();
8269
9995
  z.lights = (z.lights || []).slice();
8270
9996
  z.wheels = (z.wheels || []).slice();
@@ -8301,7 +10027,7 @@ var GameVehicles = (() => {
8301
10027
  if (owner.has(g) || !g.parent || !g.userData.glass || g.userData.ghost) return;
8302
10028
  const cg = I.localBox(c, g, inv).getCenter(new V3());
8303
10029
  let best = null;
8304
- list.forEach(function(z) {
10030
+ list2.forEach(function(z) {
8305
10031
  if (z.internal || z.type === "wheel" || z.type === "light") return;
8306
10032
  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
10033
  });
@@ -8310,7 +10036,7 @@ var GameVehicles = (() => {
8310
10036
  owner.set(g, best);
8311
10037
  }
8312
10038
  });
8313
- const looseParts = list.filter(function(z) {
10039
+ const looseParts = list2.filter(function(z) {
8314
10040
  return z.type === "part";
8315
10041
  });
8316
10042
  if (looseParts.length)
@@ -8335,7 +10061,7 @@ var GameVehicles = (() => {
8335
10061
  });
8336
10062
  }
8337
10063
  });
8338
- list.forEach(function(z) {
10064
+ list2.forEach(function(z) {
8339
10065
  if (!z.mirror) return;
8340
10066
  z.mirrors = [];
8341
10067
  const B = z.box;
@@ -8367,21 +10093,21 @@ var GameVehicles = (() => {
8367
10093
  });
8368
10094
  }
8369
10095
  });
8370
- list.forEach(function(z) {
10096
+ list2.forEach(function(z) {
8371
10097
  const B = z.box;
8372
10098
  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
10099
  });
8374
- carMap(c, list);
10100
+ carMap(c, list2);
8375
10101
  c.zoneOwners = owner;
8376
- return list;
10102
+ return list2;
8377
10103
  }
8378
- function carMap(c, list) {
10104
+ function carMap(c, list2) {
8379
10105
  const m = c.meta;
8380
10106
  const W = m.width || 1.7;
8381
10107
  const hw = W / 2;
8382
10108
  const gh = m.glasshouse;
8383
10109
  if ((m.kind || "car") !== "car" || !gh || gh.fb == null) return;
8384
- list.forEach(function(z) {
10110
+ list2.forEach(function(z) {
8385
10111
  if (z.mapSet) return;
8386
10112
  const B = z.box;
8387
10113
  const s = z.side;
@@ -8446,8 +10172,8 @@ var GameVehicles = (() => {
8446
10172
  const top = m.top || belt + 0.5;
8447
10173
  const hw = W / 2;
8448
10174
  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 || {});
10175
+ function add(id, name, type, box2, o) {
10176
+ const z = Object.assign({ id, name, type, box: I.normalizeBox(box2) }, o || {});
8451
10177
  Z.push(z);
8452
10178
  return z;
8453
10179
  }
@@ -9951,9 +11677,9 @@ var GameVehicles = (() => {
9951
11677
  const kind = kindOf2(zone);
9952
11678
  if (!kind || !zone.box) continue;
9953
11679
  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 });
11680
+ const box2 = new THREE.Box3(new THREE.Vector3(b.x0, b.y0, b.z0), new THREE.Vector3(b.x1, b.y1, b.z1));
11681
+ if (matrix) box2.applyMatrix4(matrix);
11682
+ out.push({ material: kind.material, zone: zone.id, part: kind.part, side: zone.side === 0 ? "center" : zone.side < 0 ? "left" : "right", box: box2 });
9957
11683
  }
9958
11684
  return out;
9959
11685
  };
@@ -9964,7 +11690,7 @@ var GameVehicles = (() => {
9964
11690
  const opts = options || {};
9965
11691
  if (!opts.THREE || !opts.THREE.Vector3) throw new Error("createVehicleKit: options.THREE (the three.js namespace) is required");
9966
11692
  const lod = createLod(opts.lod);
9967
- const THREE = wrapThreeForLod(opts.THREE, lod);
11693
+ const THREE = wrapThreeForLod(shapeGeometries(opts.THREE), lod);
9968
11694
  const strings = resolveStrings(opts.locale);
9969
11695
  const t = createTranslate(strings);
9970
11696
  const doc = opts.document || (typeof document !== "undefined" ? document : void 0);
@@ -10187,6 +11913,20 @@ var GameVehicles = (() => {
10187
11913
  else kit.applyArmor(c);
10188
11914
  }
10189
11915
  if (zoneState == null && st === "damaged" && kit.config.damagedScenario) zoneState = kit.zoneScenario(c, kit.config.damagedScenario);
11916
+ else if (zoneState && zoneState.scenario != null) {
11917
+ c.scenario = zoneState.scenario;
11918
+ let fromScenario = { health: {}, flags: {} };
11919
+ try {
11920
+ fromScenario = kit.zoneScenario(c, zoneState.scenario);
11921
+ } catch (e) {
11922
+ ;
11923
+ (c.zoneErrors = c.zoneErrors || []).push("scenario: " + (e && e.message));
11924
+ }
11925
+ zoneState = {
11926
+ health: Object.assign({}, fromScenario.health, zoneState.health),
11927
+ flags: Object.assign({}, fromScenario.flags, zoneState.flags)
11928
+ };
11929
+ }
10190
11930
  if (zoneState) kit.applyZones(c, zoneState);
10191
11931
  else {
10192
11932
  c.zoneState = { health: {}, flags: {} };
@@ -10225,6 +11965,51 @@ var GameVehicles = (() => {
10225
11965
  return kit;
10226
11966
  }
10227
11967
 
11968
+ // ../game-contracts/dist/index.js
11969
+ var DEFAULT_IGNITION_RULES = {
11970
+ fillCurve: [
11971
+ [0, 0.6],
11972
+ [0.1, 1],
11973
+ [0.35, 0.6],
11974
+ [0.65, 0.3],
11975
+ [0.9, 0.15],
11976
+ [1, 0.12]
11977
+ ],
11978
+ flameFillInfluence: 0.5,
11979
+ flameDefaults: { incendiary: 0.7, tracer: 0.2 },
11980
+ sparkTarget: { steel: 1, iron: 1, aluminium: 0.25 },
11981
+ sparkMetalDefault: 0.5,
11982
+ sparkFluid: { diesel: 0.15 },
11983
+ fireNear: { radius: 1.5, chancePerSecond: 0.5 }
11984
+ };
11985
+ var resolveIgnitionRules = (rules) => ({
11986
+ ...DEFAULT_IGNITION_RULES,
11987
+ ...rules,
11988
+ flameDefaults: { ...DEFAULT_IGNITION_RULES.flameDefaults, ...rules?.flameDefaults },
11989
+ sparkTarget: { ...DEFAULT_IGNITION_RULES.sparkTarget, ...rules?.sparkTarget },
11990
+ sparkFluid: { ...DEFAULT_IGNITION_RULES.sparkFluid, ...rules?.sparkFluid },
11991
+ fireNear: { ...DEFAULT_IGNITION_RULES.fireNear, ...rules?.fireNear }
11992
+ });
11993
+ var fillFactor = (fill, curve = DEFAULT_IGNITION_RULES.fillCurve) => {
11994
+ if (curve.length === 0) return 1;
11995
+ const f = Math.min(1, Math.max(0, fill));
11996
+ if (f <= curve[0][0]) return curve[0][1];
11997
+ for (let i = 1; i < curve.length; i++) {
11998
+ const [x1, y1] = curve[i];
11999
+ const [x0, y0] = curve[i - 1];
12000
+ if (f <= x1) return x1 === x0 ? y1 : y0 + (y1 - y0) * (f - x0) / (x1 - x0);
12001
+ }
12002
+ return curve[curve.length - 1][1];
12003
+ };
12004
+ var sparkTargetFactor = (materialId, metal, rules = DEFAULT_IGNITION_RULES) => !metal ? 0 : rules.sparkTarget[materialId] ?? rules.sparkMetalDefault;
12005
+ var ignitionChance = (hit, target, rules = DEFAULT_IGNITION_RULES) => {
12006
+ const k = fillFactor(target.fill, rules.fillCurve);
12007
+ const spark = (hit.ignition?.spark ?? 0) * target.sparkTarget * (target.fluid === void 0 ? 1 : rules.sparkFluid[target.fluid] ?? 1) * k;
12008
+ const flameBase = hit.ignition?.flame ?? Math.max(hit.incendiary ? rules.flameDefaults.incendiary : 0, hit.tracer ? rules.flameDefaults.tracer : 0);
12009
+ const flame = flameBase * (1 - rules.flameFillInfluence * (1 - k));
12010
+ return 1 - (1 - Math.min(1, Math.max(0, spark))) * (1 - Math.min(1, Math.max(0, flame)));
12011
+ };
12012
+
10228
12013
  // src/destructible/model.ts
10229
12014
  var JOINT_RULES = {
10230
12015
  hinge: { strength: 5e3, tools: ["wrench", "screwdriver"], seconds: 3 },
@@ -10352,19 +12137,19 @@ var GameVehicles = (() => {
10352
12137
  for (const z of zones) {
10353
12138
  let role = roleOfZone(z);
10354
12139
  if (z === hubZone) role = "frame";
10355
- const box = boxOf(z);
10356
- const area = areaOf(box.size);
12140
+ const box2 = boxOf(z);
12141
+ const area3 = areaOf(box2.size);
10357
12142
  const id = z.id;
10358
12143
  const label = String(z.name ?? id);
10359
12144
  const tags = [...z.tags ?? []];
10360
12145
  if (role === "rim") {
10361
12146
  const wheels = z.wheels ?? [];
10362
12147
  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] };
12148
+ const sz = Math.sign(box2.center[2]) || 1;
12149
+ 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
12150
  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
12151
  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]] };
12152
+ const tyreBox = { center: box2.center, size: [box2.size[0] * 0.98, box2.size[1] * 0.98, box2.size[2]] };
10368
12153
  add({
10369
12154
  id: tyreId,
10370
12155
  role: "tyre",
@@ -10384,29 +12169,29 @@ var GameVehicles = (() => {
10384
12169
  if (role === "glass") {
10385
12170
  const glass = (z.glass ?? [])[0];
10386
12171
  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] });
12172
+ add({ id, role, name: label, material: "glass", box: box2, mass: 10 * area3, hp: laminated ? 4e3 : 450, zoneId: id, visual: false, tags: ["glass", ...tags] });
10388
12173
  continue;
10389
12174
  }
10390
12175
  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] });
12176
+ add({ id, role, name: label, material: "glass", box: box2, mass: 1.2 + 4 * area3, hp: 320, zoneId: id, visual: true, tags: ["light", ...tags] });
10392
12177
  continue;
10393
12178
  }
10394
12179
  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] });
12180
+ 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
12181
  continue;
10397
12182
  }
10398
12183
  if (role === "engine") {
10399
12184
  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] });
12185
+ 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
12186
  continue;
10402
12187
  }
10403
12188
  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] });
12189
+ 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
12190
  continue;
10406
12191
  }
10407
12192
  if (role === "fuel_tank") {
10408
12193
  if (fuelless) {
10409
- add({ id, role: "other", name: label, material: "steel", box, mass: 20, hp: 25e3, zoneId: id, visual: false, tags });
12194
+ add({ id, role: "other", name: label, material: "steel", box: box2, mass: 20, hp: 25e3, zoneId: id, visual: false, tags });
10410
12195
  continue;
10411
12196
  }
10412
12197
  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 +12201,7 @@ var GameVehicles = (() => {
10416
12201
  role,
10417
12202
  name: label,
10418
12203
  material: "steel",
10419
- box,
12204
+ box: box2,
10420
12205
  mass: 14 + liters * 0.8,
10421
12206
  hp: 25e3 * armour,
10422
12207
  zoneId: id,
@@ -10429,21 +12214,21 @@ var GameVehicles = (() => {
10429
12214
  continue;
10430
12215
  }
10431
12216
  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] });
12217
+ add({ id, role, name: label, material: "plastic", box: box2, mass: 14, hp: 1500, zoneId: id, visual: true, ignitionC: 200, tags: ["battery", ...tags] });
10433
12218
  continue;
10434
12219
  }
10435
12220
  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] });
12221
+ 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
12222
  continue;
10438
12223
  }
10439
12224
  const roofLike = role === "roof";
10440
- const kg = (skin === "steel" ? STEEL_KG_M2 : ALU_KG_M2) * area * (role === "door" ? 1.6 : 1);
12225
+ const kg = (skin === "steel" ? STEEL_KG_M2 : ALU_KG_M2) * area3 * (role === "door" ? 1.6 : 1);
10441
12226
  add({
10442
12227
  id,
10443
12228
  role,
10444
12229
  name: label,
10445
12230
  material: skin,
10446
- box,
12231
+ box: box2,
10447
12232
  mass: Math.max(2, kg),
10448
12233
  hp: (roofLike ? 3e4 : 2e4) * armour,
10449
12234
  zoneId: id,
@@ -10665,7 +12450,7 @@ var GameVehicles = (() => {
10665
12450
  const my = kind === "bike" || kind === "quad" ? (m.top ?? belt + 0.25) - 0.1 : belt + 0.12;
10666
12451
  for (const s of [-1, 1]) {
10667
12452
  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] };
12453
+ let box2 = { center: [mx, my, s * half], size: [0.14, 0.12, 0.2] };
10669
12454
  if (door) {
10670
12455
  const bb = door.mirrors.reduce(
10671
12456
  (acc, o) => {
@@ -10678,14 +12463,14 @@ var GameVehicles = (() => {
10678
12463
  },
10679
12464
  null
10680
12465
  );
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])] };
12466
+ 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
12467
  }
10683
12468
  add({
10684
12469
  id: `mirror_${side(s)}`,
10685
12470
  role: "mirror",
10686
12471
  name: t("mechanics.mirror", { side: t(`sides.${side(s)}`), sideM: t(`sides.${side(s)}M`) }),
10687
12472
  material: "plastic",
10688
- box,
12473
+ box: box2,
10689
12474
  mass: 1.1,
10690
12475
  hp: 1500,
10691
12476
  ignitionC: 400,
@@ -10875,13 +12660,13 @@ var GameVehicles = (() => {
10875
12660
  (parent ?? this.c.vehicle).add(m);
10876
12661
  return m;
10877
12662
  }
10878
- surface(axis, sign, B, samples) {
12663
+ surface(axis, sign2, B, samples) {
10879
12664
  const V3 = this.THREE.Vector3;
10880
12665
  const hits = [];
10881
12666
  const far = 3;
10882
12667
  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);
12668
+ 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);
12669
+ const d = axis === "z" ? new V3(0, 0, -sign2) : new V3(-sign2, 0, 0);
10885
12670
  const h = this.I.probe(this.c, o, d, far);
10886
12671
  if (h) hits.push(axis === "z" ? h.p.z : h.p.x);
10887
12672
  }
@@ -11031,12 +12816,12 @@ var GameVehicles = (() => {
11031
12816
  const z = this.zone(p);
11032
12817
  if (!z) return false;
11033
12818
  const V3 = this.THREE.Vector3;
11034
- const list = z.glass.filter((g) => g.parent && g.visible);
11035
- if (!list.length) return false;
12819
+ const list2 = z.glass.filter((g) => g.parent && g.visible);
12820
+ if (!list2.length) return false;
11036
12821
  const pt = new V3(...point);
11037
- let best = list[0];
12822
+ let best = list2[0];
11038
12823
  let bd = Infinity;
11039
- for (const g of list) {
12824
+ for (const g of list2) {
11040
12825
  g.geometry.computeBoundingBox();
11041
12826
  const bb = g.geometry.boundingBox.clone().applyMatrix4(g.matrix);
11042
12827
  const d = bb.distanceToPoint(pt);
@@ -11337,6 +13122,9 @@ var GameVehicles = (() => {
11337
13122
  const headless = !!options.headless;
11338
13123
  const seedBase = strHash(`${c.key}|${id}|${options.seed ?? 0}`);
11339
13124
  let rand = rng(seedBase);
13125
+ const ignitionRules = resolveIgnitionRules(options.ignition);
13126
+ const fuelFraction = clamp(options.fuel ?? 1, 0, 1);
13127
+ let igniteRand = rng(strHash(`${c.key}|${id}|ignition|${options.seed ?? 0}`));
11340
13128
  let clock = 0;
11341
13129
  let model;
11342
13130
  let vis = null;
@@ -11404,7 +13192,7 @@ var GameVehicles = (() => {
11404
13192
  dents: [],
11405
13193
  holes: [],
11406
13194
  cracks: [],
11407
- liters: d.contains?.liters,
13195
+ liters: d.contains ? d.role === "fuel_tank" ? d.contains.liters * fuelFraction : d.contains.liters : void 0,
11408
13196
  lost: false,
11409
13197
  status: "intact",
11410
13198
  flat: false,
@@ -11422,6 +13210,7 @@ var GameVehicles = (() => {
11422
13210
  wasLit = true;
11423
13211
  deadEngine = false;
11424
13212
  rand = rng(seedBase);
13213
+ igniteRand = rng(strHash(`${c.key}|${id}|ignition|${options.seed ?? 0}`));
11425
13214
  steerSign = rand() < 0.5 ? -1 : 1;
11426
13215
  vis = headless ? null : new VehicleVisuals(kit, c, model);
11427
13216
  recomputeGroups();
@@ -11443,8 +13232,8 @@ var GameVehicles = (() => {
11443
13232
  comps.get(r).push(p);
11444
13233
  }
11445
13234
  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);
13235
+ const list2 = [...comps.entries()].sort((a, b) => a[0] === hubRoot ? -1 : b[0] === hubRoot ? 1 : 0);
13236
+ groupsCache = list2.map(([, v]) => v);
11448
13237
  groupOf = /* @__PURE__ */ new Map();
11449
13238
  groupsCache.forEach((g, i) => g.forEach((p) => groupOf.set(p, i)));
11450
13239
  return groupsCache;
@@ -11716,8 +13505,19 @@ var GameVehicles = (() => {
11716
13505
  if (!p.burning && p.temp > AMBIENT) p.temp = Math.max(AMBIENT, p.temp - dt * 6);
11717
13506
  }
11718
13507
  for (const f of flames) heat(centreOf(f.def), 0.8, 500, dt * 0.5, out, 0);
13508
+ fireNearLeak(flames, dt, out);
11719
13509
  thermal(out);
11720
13510
  };
13511
+ const fireNearLeak = (flames, dt, out) => {
13512
+ const tank = tankPart();
13513
+ const leak = tank ? leaks.get(tank.def.id) : void 0;
13514
+ if (!tank || !leak || tank.lost || tank.burning || exploded || leak.fluid !== "fuel" && leak.fluid !== "diesel") return;
13515
+ if (leak.pud?.fire) return light(tank, out);
13516
+ const { radius, chancePerSecond } = ignitionRules.fireNear;
13517
+ if (chancePerSecond <= 0) return;
13518
+ const near = flames.some((f) => f !== tank && distToBox(tank.def, centreOf(f.def)) - Math.max(...f.def.box.size) / 2 <= radius);
13519
+ if (near && igniteRand() < 1 - Math.exp(-chancePerSecond * dt)) light(tank, out);
13520
+ };
11721
13521
  const normalOf = (d, v) => faceNormal(d, v);
11722
13522
  const ballistic = (h, out) => {
11723
13523
  sync();
@@ -11785,10 +13585,21 @@ var GameVehicles = (() => {
11785
13585
  let amount = through ? E * 0.6 : E * 0.25;
11786
13586
  if (through && (d.role === "battery" || d.role === "alternator")) amount = Math.max(amount, p.hp * 0.65);
11787
13587
  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);
13588
+ if (through && d.role === "fuel_tank") rollIgnition(p, h, metal, out);
11789
13589
  }
11790
13590
  }
11791
13591
  };
13592
+ const light = (tank, out) => {
13593
+ tank.temp = Math.max(tank.temp, (tank.def.ignitionC ?? 260) + 1);
13594
+ if ((tank.liters ?? 0) > 0.5) ignite(tank, out);
13595
+ };
13596
+ const rollIgnition = (tank, h, metal, out) => {
13597
+ const d = tank.def;
13598
+ if (!d.contains || tank.burning || (tank.liters ?? 0) <= 0.05) return;
13599
+ const fill = d.contains.liters > 0 ? (tank.liters ?? 0) / d.contains.liters : 0;
13600
+ const chance = ignitionChance(h, { sparkTarget: sparkTargetFactor(d.material, metal, ignitionRules), fill, fluid: d.contains.fluid }, ignitionRules);
13601
+ if (chance > 0 && igniteRand() < chance) light(tank, out);
13602
+ };
11792
13603
  const blunt = (h, out) => {
11793
13604
  sync();
11794
13605
  const M = toFrameMatrix();
@@ -11852,7 +13663,7 @@ var GameVehicles = (() => {
11852
13663
  const verdict = kit.blastDamage(c, E / (1 + dist * dist * 4) / 50);
11853
13664
  if (verdict === "explosion") explode(tank, h.source, out);
11854
13665
  else if (verdict === "fire") {
11855
- tank.temp = Math.max(tank.temp, tank.def.ignitionC ?? 260);
13666
+ light(tank, out);
11856
13667
  startLeak(tank, centreOf(tank.def).setY(tank.def.box.center[1] - tank.def.box.size[1] / 2 + 0.05), 12, out, dir);
11857
13668
  }
11858
13669
  }
@@ -11866,9 +13677,9 @@ var GameVehicles = (() => {
11866
13677
  thermal(out);
11867
13678
  };
11868
13679
  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);
13680
+ const list2 = [...ps.values()].filter((p) => p.def.role === role);
13681
+ if (!list2.length) return true;
13682
+ return list2.every((p) => !p.lost && attached(p.def.id) && p.hp >= p.def.hp * 0.5);
11872
13683
  };
11873
13684
  const tankPart = () => [...ps.values()].find((p) => p.def.role === "fuel_tank" && p.def.contains);
11874
13685
  const computeStatus = () => {
@@ -12060,8 +13871,8 @@ var GameVehicles = (() => {
12060
13871
  queue = [];
12061
13872
  const hood = c.hood;
12062
13873
  if (hood) {
12063
- const sign = hood.sign || 1;
12064
- const cur = hood.piv.rotation.z / sign;
13874
+ const sign2 = hood.sign || 1;
13875
+ const cur = hood.piv.rotation.z / sign2;
12065
13876
  if (Math.abs(cur - hood.ang) > 1e-4) {
12066
13877
  hood.ang = cur;
12067
13878
  hood.target = cur;
@@ -12114,7 +13925,7 @@ var GameVehicles = (() => {
12114
13925
  const reset = () => {
12115
13926
  const old = c;
12116
13927
  const parent = old.root.parent;
12117
- const next = kit.build(old.key, old.state);
13928
+ const next = kit.build(old.key, old.state, old.scenario != null ? { scenario: old.scenario } : void 0);
12118
13929
  if (parent) {
12119
13930
  next.root.position.copy(old.root.position);
12120
13931
  next.root.quaternion.copy(old.root.quaternion);
@@ -12171,7 +13982,8 @@ var GameVehicles = (() => {
12171
13982
  // src/destructible/item.ts
12172
13983
  function buildVehicleItem(kit, key, options = {}) {
12173
13984
  const THREE = kit.THREE;
12174
- const vehicle = options.lod ? kit.build(key, options.state ?? "intact", void 0, { lod: true }) : kit.build(key, options.state ?? "intact");
13985
+ const zoneState = options.scenario != null ? { scenario: options.scenario } : void 0;
13986
+ const vehicle = options.lod ? kit.build(key, options.state ?? "intact", zoneState, { lod: true }) : kit.build(key, options.state ?? "intact", zoneState);
12175
13987
  const m = vehicle.meta;
12176
13988
  const root = new THREE.Group();
12177
13989
  root.name = key;