@adatechnology/game-vehicles 0.4.5 → 0.4.7

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.
@@ -580,12 +580,12 @@ var GameVehicles = (() => {
580
580
  return triangles;
581
581
  }
582
582
  };
583
- function linkedList(data, start, end, dim, clockwise) {
583
+ function linkedList(data, start2, end, dim, clockwise) {
584
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);
585
+ if (clockwise === signedArea(data, start2, end, dim) > 0) {
586
+ for (i = start2; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last);
587
587
  } else {
588
- for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last);
588
+ for (i = end - dim; i >= start2; i -= dim) last = insertNode(i, data[i], data[i + 1], last);
589
589
  }
590
590
  if (last && equals(last, last.next)) {
591
591
  removeNode(last);
@@ -593,10 +593,10 @@ var GameVehicles = (() => {
593
593
  }
594
594
  return last;
595
595
  }
596
- function filterPoints(start, end) {
597
- if (!start) return start;
598
- if (!end) end = start;
599
- let p = start, again;
596
+ function filterPoints(start2, end) {
597
+ if (!start2) return start2;
598
+ if (!end) end = start2;
599
+ let p = start2, again;
600
600
  do {
601
601
  again = false;
602
602
  if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
@@ -675,8 +675,8 @@ var GameVehicles = (() => {
675
675
  }
676
676
  return true;
677
677
  }
678
- function cureLocalIntersections(start, triangles, dim) {
679
- let p = start;
678
+ function cureLocalIntersections(start2, triangles, dim) {
679
+ let p = start2;
680
680
  do {
681
681
  const a = p.prev, b = p.next.next;
682
682
  if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
@@ -685,14 +685,14 @@ var GameVehicles = (() => {
685
685
  triangles.push(b.i / dim | 0);
686
686
  removeNode(p);
687
687
  removeNode(p.next);
688
- p = start = b;
688
+ p = start2 = b;
689
689
  }
690
690
  p = p.next;
691
- } while (p !== start);
691
+ } while (p !== start2);
692
692
  return filterPoints(p);
693
693
  }
694
- function splitEarcut(start, triangles, dim, minX, minY, invSize) {
695
- let a = start;
694
+ function splitEarcut(start2, triangles, dim, minX, minY, invSize) {
695
+ let a = start2;
696
696
  do {
697
697
  let b = a.next.next;
698
698
  while (b !== a.prev) {
@@ -707,15 +707,15 @@ var GameVehicles = (() => {
707
707
  b = b.next;
708
708
  }
709
709
  a = a.next;
710
- } while (a !== start);
710
+ } while (a !== start2);
711
711
  }
712
712
  function eliminateHoles(data, holeIndices, outerNode, dim) {
713
713
  const queue = [];
714
- let i, len, start, end, list2;
714
+ let i, len, start2, end, list2;
715
715
  for (i = 0, len = holeIndices.length; i < len; i++) {
716
- start = holeIndices[i] * dim;
716
+ start2 = holeIndices[i] * dim;
717
717
  end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
718
- list2 = linkedList(data, start, end, dim, false);
718
+ list2 = linkedList(data, start2, end, dim, false);
719
719
  if (list2 === list2.next) list2.steiner = true;
720
720
  queue.push(getLeftmost(list2));
721
721
  }
@@ -770,14 +770,14 @@ var GameVehicles = (() => {
770
770
  function sectorContainsSector(m, p) {
771
771
  return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0;
772
772
  }
773
- function indexCurve(start, minX, minY, invSize) {
774
- let p = start;
773
+ function indexCurve(start2, minX, minY, invSize) {
774
+ let p = start2;
775
775
  do {
776
776
  if (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize);
777
777
  p.prevZ = p.prev;
778
778
  p.nextZ = p.next;
779
779
  p = p.next;
780
- } while (p !== start);
780
+ } while (p !== start2);
781
781
  p.prevZ.nextZ = null;
782
782
  p.prevZ = null;
783
783
  sortLinked(p);
@@ -834,12 +834,12 @@ var GameVehicles = (() => {
834
834
  y = (y | y << 1) & 1431655765;
835
835
  return x | y << 1;
836
836
  }
837
- function getLeftmost(start) {
838
- let p = start, leftmost = start;
837
+ function getLeftmost(start2) {
838
+ let p = start2, leftmost = start2;
839
839
  do {
840
840
  if (p.x < leftmost.x || p.x === leftmost.x && p.y < leftmost.y) leftmost = p;
841
841
  p = p.next;
842
- } while (p !== start);
842
+ } while (p !== start2);
843
843
  return leftmost;
844
844
  }
845
845
  function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
@@ -938,9 +938,9 @@ var GameVehicles = (() => {
938
938
  this.nextZ = null;
939
939
  this.steiner = false;
940
940
  }
941
- function signedArea(data, start, end, dim) {
941
+ function signedArea(data, start2, end, dim) {
942
942
  let sum = 0;
943
- for (let i = start, j = end - dim; i < end; i += dim) {
943
+ for (let i = start2, j = end - dim; i < end; i += dim) {
944
944
  sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
945
945
  j = i;
946
946
  }
@@ -1197,7 +1197,7 @@ var GameVehicles = (() => {
1197
1197
  const uvs = [uv(iA), uv(iB), uv(iC), uv(iD)];
1198
1198
  for (const k of [0, 1, 3, 1, 2, 3]) addUV(...uvs[k]);
1199
1199
  };
1200
- let start = verticesArray.length / 3;
1200
+ let start2 = verticesArray.length / 3;
1201
1201
  if (bevelEnabled) {
1202
1202
  let offset = 0;
1203
1203
  for (let i = 0; i < flen; i++) {
@@ -1220,8 +1220,8 @@ var GameVehicles = (() => {
1220
1220
  f3(face[0] + vlen * steps, face[1] + vlen * steps, face[2] + vlen * steps);
1221
1221
  }
1222
1222
  }
1223
- groups.push({ start, count: verticesArray.length / 3 - start, materialIndex: 0 });
1224
- start = verticesArray.length / 3;
1223
+ groups.push({ start: start2, count: verticesArray.length / 3 - start2, materialIndex: 0 });
1224
+ start2 = verticesArray.length / 3;
1225
1225
  const sidewalls = (ring2, layeroffset2) => {
1226
1226
  let i = ring2.length;
1227
1227
  while (--i >= 0) {
@@ -1242,7 +1242,7 @@ var GameVehicles = (() => {
1242
1242
  sidewalls(ahole, layeroffset);
1243
1243
  layeroffset += ahole.length;
1244
1244
  }
1245
- groups.push({ start, count: verticesArray.length / 3 - start, materialIndex: 1 });
1245
+ groups.push({ start: start2, count: verticesArray.length / 3 - start2, materialIndex: 1 });
1246
1246
  const positions = new Float32Array(verticesArray);
1247
1247
  return { type: "ExtrudeGeometry", parameters, positions, normals: computeVertexNormals(positions), uvs: new Float32Array(uvArray), groups };
1248
1248
  }
@@ -1518,8 +1518,8 @@ var GameVehicles = (() => {
1518
1518
  var shims = /* @__PURE__ */ new WeakSet();
1519
1519
  function shapeGeometries(THREE, options = {}) {
1520
1520
  if (shims.has(THREE)) return THREE;
1521
- const cache = options.cache !== false;
1522
- const key = cache ? "cache" : "nocache";
1521
+ const cache2 = options.cache !== false;
1522
+ const key = cache2 ? "cache" : "nocache";
1523
1523
  let byKey = namespaces.get(THREE);
1524
1524
  if (!byKey) {
1525
1525
  byKey = /* @__PURE__ */ new Map();
@@ -1528,7 +1528,7 @@ var GameVehicles = (() => {
1528
1528
  const existing = byKey.get(key);
1529
1529
  if (existing) return existing;
1530
1530
  const made = (p) => toGeometry(THREE, p);
1531
- const viaCache = (node, direct) => cache ? buildPrimitive(node) : direct();
1531
+ const viaCache = (node, direct) => cache2 ? buildPrimitive(node) : direct();
1532
1532
  const defined = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
1533
1533
  const constructors = {
1534
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))),
@@ -2224,7 +2224,7 @@ var GameVehicles = (() => {
2224
2224
  const I = kit._;
2225
2225
  const document2 = kit.document;
2226
2226
  const RUST = new THREE.Color("#5e3220");
2227
- const cache = {};
2227
+ const cache2 = {};
2228
2228
  let envTex = null;
2229
2229
  function studioEnvironment() {
2230
2230
  if (envTex) return envTex;
@@ -2269,7 +2269,7 @@ var GameVehicles = (() => {
2269
2269
  const key = "s" + c.getHexString() + JSON.stringify(o, function(k, v) {
2270
2270
  return v && v.isColor ? "#" + v.getHexString() : v && v.isTexture ? v.uuid : v;
2271
2271
  });
2272
- if (!cache[key]) {
2272
+ if (!cache2[key]) {
2273
2273
  const o2 = Object.assign({}, o);
2274
2274
  delete o2.noEnv;
2275
2275
  const mt = new THREE.MeshStandardMaterial(Object.assign({ color: c, roughness: 0.8, metalness: 0, flatShading: false }, o2));
@@ -2277,9 +2277,9 @@ var GameVehicles = (() => {
2277
2277
  mt.envMap = studioEnvironment();
2278
2278
  if (o.envMapIntensity == null) mt.envMapIntensity = 0.55 + (mt.metalness - 0.6) * 1.6;
2279
2279
  }
2280
- cache[key] = mt;
2280
+ cache2[key] = mt;
2281
2281
  }
2282
- return cache[key];
2282
+ return cache2[key];
2283
2283
  }
2284
2284
  function makeFlatMaterial(color2, o) {
2285
2285
  return makeMaterial(color2, Object.assign({ flatShading: true, roughness: 0.92 }, o || {}));
@@ -2394,7 +2394,131 @@ var GameVehicles = (() => {
2394
2394
  return t;
2395
2395
  }
2396
2396
  kit.textTexture = textTexture;
2397
- Object.assign(I, { RUST, cache, studioEnvironment, makeMaterial, makeFlatMaterial, DOUBLE_SIDE, toColor, MATERIALS, paintColor, texCache, textTexture });
2397
+ Object.assign(I, { RUST, cache: cache2, studioEnvironment, makeMaterial, makeFlatMaterial, DOUBLE_SIDE, toColor, MATERIALS, paintColor, texCache, textTexture });
2398
+ }
2399
+
2400
+ // src/tri-index.ts
2401
+ var TRI_BLOCK = 32;
2402
+ var cache = /* @__PURE__ */ new WeakMap();
2403
+ function trianglesOf(geo) {
2404
+ return Math.floor((geo.index ? geo.index.count : geo.attributes.position.count) / 3);
2405
+ }
2406
+ function supports(geo) {
2407
+ const pos = geo.attributes && geo.attributes.position;
2408
+ return !!pos && !pos.isInterleavedBufferAttribute && pos.itemSize === 3;
2409
+ }
2410
+ function isCurrent(geo, ti) {
2411
+ const pos = geo.attributes.position;
2412
+ return ti.position === pos && ti.positionVersion === pos.version && ti.index === (geo.index || null) && ti.indexVersion === (geo.index ? geo.index.version : 0) && ti.triangles === trianglesOf(geo);
2413
+ }
2414
+ function blockCount(triangles) {
2415
+ return Math.ceil(triangles / TRI_BLOCK);
2416
+ }
2417
+ function boxBlock(ti, block) {
2418
+ const B = ti.boxes;
2419
+ const t0 = block * TRI_BLOCK;
2420
+ const t1 = Math.min(ti.triangles, t0 + TRI_BLOCK);
2421
+ const o = block * 6;
2422
+ for (let k = 0; k < 3; k++) {
2423
+ ti.blocks[o + k] = Infinity;
2424
+ ti.blocks[o + 3 + k] = -Infinity;
2425
+ }
2426
+ for (let t = t0; t < t1; t++) {
2427
+ const j = t * 6;
2428
+ for (let k = 0; k < 3; k++) {
2429
+ if (B[j + k] < ti.blocks[o + k]) ti.blocks[o + k] = B[j + k];
2430
+ if (B[j + 3 + k] > ti.blocks[o + 3 + k]) ti.blocks[o + 3 + k] = B[j + 3 + k];
2431
+ }
2432
+ }
2433
+ }
2434
+ function boxTriangle(geo, ti, t) {
2435
+ const P = geo.attributes.position.array;
2436
+ const ix = geo.index ? geo.index.array : null;
2437
+ const a = (ix ? ix[t * 3] : t * 3) * 3;
2438
+ const b = (ix ? ix[t * 3 + 1] : t * 3 + 1) * 3;
2439
+ const c = (ix ? ix[t * 3 + 2] : t * 3 + 2) * 3;
2440
+ const j = t * 6;
2441
+ for (let k = 0; k < 3; k++) {
2442
+ ti.boxes[j + k] = Math.min(P[a + k], P[b + k], P[c + k]);
2443
+ ti.boxes[j + 3 + k] = Math.max(P[a + k], P[b + k], P[c + k]);
2444
+ }
2445
+ }
2446
+ function advance(geo, ti, deadline) {
2447
+ const total = blockCount(ti.triangles);
2448
+ while (ti.builtBlocks < total) {
2449
+ const block = ti.builtBlocks;
2450
+ const t1 = Math.min(ti.triangles, (block + 1) * TRI_BLOCK);
2451
+ for (let t = block * TRI_BLOCK; t < t1; t++) boxTriangle(geo, ti, t);
2452
+ boxBlock(ti, block);
2453
+ ti.builtBlocks++;
2454
+ if (deadline !== Infinity && performance.now() >= deadline) break;
2455
+ }
2456
+ return ti.builtBlocks === total;
2457
+ }
2458
+ function start(geo) {
2459
+ const pos = geo.attributes.position;
2460
+ const triangles = trianglesOf(geo);
2461
+ const ti = {
2462
+ position: pos,
2463
+ positionVersion: pos.version,
2464
+ index: geo.index || null,
2465
+ indexVersion: geo.index ? geo.index.version : 0,
2466
+ triangles,
2467
+ boxes: new Float64Array(triangles * 6),
2468
+ blocks: new Float64Array(blockCount(triangles) * 6),
2469
+ builtBlocks: 0
2470
+ };
2471
+ cache.set(geo, ti);
2472
+ return ti;
2473
+ }
2474
+ function triIndexOf(geo) {
2475
+ if (!supports(geo)) return void 0;
2476
+ let ti = cache.get(geo);
2477
+ if (!ti || !isCurrent(geo, ti)) ti = start(geo);
2478
+ advance(geo, ti, Infinity);
2479
+ return ti;
2480
+ }
2481
+ function warmTriIndex(geo, deadline) {
2482
+ if (!supports(geo)) return true;
2483
+ let ti = cache.get(geo);
2484
+ if (!ti || !isCurrent(geo, ti)) ti = start(geo);
2485
+ return advance(geo, ti, deadline);
2486
+ }
2487
+ function storeTriIndex(geo, boxes) {
2488
+ const ti = start(geo);
2489
+ ti.boxes = boxes;
2490
+ const total = blockCount(ti.triangles);
2491
+ for (let b = 0; b < total; b++) boxBlock(ti, b);
2492
+ ti.builtBlocks = total;
2493
+ }
2494
+ function liveTriIndex(geo) {
2495
+ if (geo.index || !supports(geo)) return void 0;
2496
+ const ti = cache.get(geo);
2497
+ return ti && isCurrent(geo, ti) && ti.builtBlocks === blockCount(ti.triangles) ? ti : void 0;
2498
+ }
2499
+ function touchTriIndex(geo, ti, vertices) {
2500
+ if (ti.positionVersion + 1 !== geo.attributes.position.version) return;
2501
+ const blocks = /* @__PURE__ */ new Set();
2502
+ for (const v of vertices) {
2503
+ const t = Math.floor(v / 3);
2504
+ if (t >= ti.triangles) continue;
2505
+ boxTriangle(geo, ti, t);
2506
+ blocks.add(Math.floor(t / TRI_BLOCK));
2507
+ }
2508
+ blocks.forEach((b) => boxBlock(ti, b));
2509
+ ti.positionVersion = geo.attributes.position.version;
2510
+ }
2511
+ function boundsFromIndex(geo) {
2512
+ if (geo.index || !supports(geo)) return void 0;
2513
+ const ti = cache.get(geo);
2514
+ if (!ti || !isCurrent(geo, ti) || ti.builtBlocks !== blockCount(ti.triangles) || !ti.triangles || geo.attributes.position.count !== ti.triangles * 3) return void 0;
2515
+ const box2 = new Float64Array([Infinity, Infinity, Infinity, -Infinity, -Infinity, -Infinity]);
2516
+ for (let b = 0, n = blockCount(ti.triangles); b < n; b++)
2517
+ for (let k = 0; k < 3; k++) {
2518
+ box2[k] = Math.min(box2[k], ti.blocks[b * 6 + k]);
2519
+ box2[3 + k] = Math.max(box2[3 + k], ti.blocks[b * 6 + 3 + k]);
2520
+ }
2521
+ return { box: box2, version: ti.positionVersion };
2398
2522
  }
2399
2523
 
2400
2524
  // src/geometry.ts
@@ -2491,7 +2615,8 @@ var GameVehicles = (() => {
2491
2615
  g.userData.crease = true;
2492
2616
  return g;
2493
2617
  }
2494
- function crease(geo, deg) {
2618
+ const latestCrease = /* @__PURE__ */ new WeakMap();
2619
+ function creaseTask(geo, deg) {
2495
2620
  if (geo.index) {
2496
2621
  const ni = geo.toNonIndexed();
2497
2622
  Object.keys(ni.attributes).forEach(function(k) {
@@ -2499,30 +2624,22 @@ var GameVehicles = (() => {
2499
2624
  });
2500
2625
  geo.setIndex(null);
2501
2626
  }
2502
- const p = geo.attributes.position;
2503
- const n = p.count;
2627
+ const P = geo.attributes.position.array;
2628
+ const n = geo.attributes.position.count;
2504
2629
  const faces = n / 3;
2505
2630
  const FN = new Float64Array(faces * 3);
2506
- const map = /* @__PURE__ */ new Map();
2507
- const RANGE = 65536;
2508
- function key(i) {
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;
2514
- }
2515
- const keys = new Array(n);
2631
+ const out = new Float32Array(n * 3);
2516
2632
  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);
2633
+ const o = 9 * f;
2634
+ const ax = P[o];
2635
+ const ay = P[o + 1];
2636
+ const az = P[o + 2];
2637
+ const bx = P[o + 3];
2638
+ const by = P[o + 4];
2639
+ const bz = P[o + 5];
2640
+ const cx = P[o + 6];
2641
+ const cy = P[o + 7];
2642
+ const cz = P[o + 8];
2526
2643
  const e1x = cx - bx;
2527
2644
  const e1y = cy - by;
2528
2645
  const e1z = cz - bz;
@@ -2542,41 +2659,109 @@ var GameVehicles = (() => {
2542
2659
  FN[3 * f + 1] = ny;
2543
2660
  FN[3 * f + 2] = nz;
2544
2661
  for (let k = 0; k < 3; k++) {
2545
- const kk = key(3 * f + k);
2546
- keys[3 * f + k] = kk;
2547
- const list2 = map.get(kk);
2548
- if (list2) list2.push(f);
2549
- else map.set(kk, [f]);
2662
+ out[9 * f + 3 * k] = nx;
2663
+ out[9 * f + 3 * k + 1] = ny;
2664
+ out[9 * f + 3 * k + 2] = nz;
2665
+ }
2666
+ }
2667
+ const normal = new THREE.BufferAttribute(out, 3);
2668
+ geo.setAttribute("normal", normal);
2669
+ const token = {};
2670
+ latestCrease.set(geo, token);
2671
+ let size = 16;
2672
+ while (size < n * 2) size *= 2;
2673
+ const mask = size - 1;
2674
+ const slot = new Int32Array(size).fill(-1);
2675
+ const gx = new Int32Array(n);
2676
+ const gy = new Int32Array(n);
2677
+ const gz = new Int32Array(n);
2678
+ const groupOf = new Int32Array(n);
2679
+ let groups = 0;
2680
+ let start2;
2681
+ let members;
2682
+ const cs = Math.cos(deg * PI / 180);
2683
+ let phase = 0;
2684
+ let at = 0;
2685
+ function hashSlice(deadline) {
2686
+ for (; at < n; at++) {
2687
+ if ((at & 1023) === 0 && performance.now() > deadline) return false;
2688
+ const x = Math.round(P[3 * at] * 2e3) | 0;
2689
+ const y = Math.round(P[3 * at + 1] * 2e3) | 0;
2690
+ const z = Math.round(P[3 * at + 2] * 2e3) | 0;
2691
+ let h = (Math.imul(x, 73856093) ^ Math.imul(y, 19349663) ^ Math.imul(z, 83492791)) & mask;
2692
+ let g = slot[h];
2693
+ while (g >= 0 && (gx[g] !== x || gy[g] !== y || gz[g] !== z)) {
2694
+ h = h + 1 & mask;
2695
+ g = slot[h];
2696
+ }
2697
+ if (g < 0) {
2698
+ g = groups++;
2699
+ slot[h] = g;
2700
+ gx[g] = x;
2701
+ gy[g] = y;
2702
+ gz[g] = z;
2703
+ }
2704
+ groupOf[at] = g;
2550
2705
  }
2706
+ return true;
2551
2707
  }
2552
- const cs = Math.cos(deg * PI / 180);
2553
- const out = new Float32Array(n * 3);
2554
- for (let i = 0; i < n; i++) {
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];
2708
+ function gather() {
2709
+ start2 = new Int32Array(groups + 1);
2710
+ for (let i = 0; i < n; i++) start2[groupOf[i] + 1]++;
2711
+ for (let g = 0; g < groups; g++) start2[g + 1] += start2[g];
2712
+ const fill = start2.slice(0, groups);
2713
+ members = new Int32Array(n);
2714
+ for (let i = 0; i < n; i++) members[fill[groupOf[i]]++] = i / 3 | 0;
2715
+ }
2716
+ function smoothSlice(deadline) {
2717
+ for (; at < n; at++) {
2718
+ if ((at & 1023) === 0 && performance.now() > deadline) return false;
2719
+ const fi = (at / 3 | 0) * 3;
2720
+ const g = groupOf[at];
2721
+ let sx = 0;
2722
+ let sy = 0;
2723
+ let sz = 0;
2724
+ for (let j = start2[g], end = start2[g + 1]; j < end; j++) {
2725
+ const o = members[j] * 3;
2726
+ if (FN[o] * FN[fi] + FN[o + 1] * FN[fi + 1] + FN[o + 2] * FN[fi + 2] >= cs) {
2727
+ sx += FN[o];
2728
+ sy += FN[o + 1];
2729
+ sz += FN[o + 2];
2730
+ }
2566
2731
  }
2732
+ if (sx * sx + sy * sy + sz * sz < 1e-20) {
2733
+ sx = FN[fi];
2734
+ sy = FN[fi + 1];
2735
+ sz = FN[fi + 2];
2736
+ }
2737
+ const inv = 1 / (Math.sqrt(sx * sx + sy * sy + sz * sz) || 1);
2738
+ out[3 * at] = sx * inv;
2739
+ out[3 * at + 1] = sy * inv;
2740
+ out[3 * at + 2] = sz * inv;
2567
2741
  }
2568
- if (sx * sx + sy * sy + sz * sz < 1e-20) {
2569
- sx = FN[fi];
2570
- sy = FN[fi + 1];
2571
- sz = FN[fi + 2];
2742
+ return true;
2743
+ }
2744
+ function step(deadline) {
2745
+ if (latestCrease.get(geo) !== token) return true;
2746
+ if (phase === 0) {
2747
+ if (!hashSlice(deadline)) return false;
2748
+ gather();
2749
+ phase = 1;
2750
+ at = 0;
2751
+ }
2752
+ if (phase === 1) {
2753
+ const done = smoothSlice(deadline);
2754
+ if (deadline !== Infinity) normal.needsUpdate = true;
2755
+ if (!done) return false;
2756
+ phase = 2;
2757
+ geo.userData.crease = true;
2572
2758
  }
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;
2759
+ return true;
2577
2760
  }
2578
- geo.setAttribute("normal", new THREE.BufferAttribute(out, 3));
2579
- geo.userData.crease = true;
2761
+ return { geo, step };
2762
+ }
2763
+ function crease(geo, deg) {
2764
+ creaseTask(geo, deg).step(Infinity);
2580
2765
  return geo;
2581
2766
  }
2582
2767
  function pathShape(items) {
@@ -2723,28 +2908,28 @@ var GameVehicles = (() => {
2723
2908
  return g;
2724
2909
  }
2725
2910
  function cutGeometry(geo, tests) {
2726
- const src = geo.index ? geo.toNonIndexed() : geo;
2911
+ const idx = geo.index ? geo.index.array : null;
2727
2912
  const names = ["position"].concat(
2728
2913
  ["color", "uv"].filter(function(n2) {
2729
- return !!src.attributes[n2];
2914
+ return !!geo.attributes[n2];
2730
2915
  })
2731
2916
  );
2732
2917
  const sizes = names.map(function(n2) {
2733
- return src.attributes[n2].itemSize;
2918
+ return geo.attributes[n2].itemSize;
2734
2919
  });
2735
2920
  const arrs = names.map(function(n2) {
2736
- return src.attributes[n2].array;
2921
+ return geo.attributes[n2].array;
2737
2922
  });
2738
- const nv = src.attributes.position.count;
2739
- let tris = [];
2740
- for (let f = 0; f < nv; f += 3) {
2923
+ const nv = idx ? idx.length : geo.attributes.position.count;
2924
+ function readTri(f) {
2741
2925
  const t = [];
2742
2926
  for (let k = 0; k < 3; k++) {
2927
+ const vi = idx ? idx[f + k] : f + k;
2743
2928
  const v = [];
2744
- for (let a = 0; a < names.length; a++) for (let q = 0; q < sizes[a]; q++) v.push(arrs[a][(f + k) * sizes[a] + q]);
2929
+ for (let a = 0; a < names.length; a++) for (let q = 0; q < sizes[a]; q++) v.push(arrs[a][vi * sizes[a] + q]);
2745
2930
  t.push(v);
2746
2931
  }
2747
- tris.push(t);
2932
+ return t;
2748
2933
  }
2749
2934
  const cut = {};
2750
2935
  const n = new V3();
@@ -2770,60 +2955,177 @@ var GameVehicles = (() => {
2770
2955
  function fan(poly, arr) {
2771
2956
  for (let i = 1; i < poly.length - 1; i++) arr.push([poly[0], poly[i], poly[i + 1]]);
2772
2957
  }
2773
- tests.forEach(function(T) {
2774
- const next = [];
2958
+ function testTri(T, t, next, rem) {
2959
+ const A = t[0];
2960
+ const B = t[1];
2961
+ const C = t[2];
2962
+ e1.set(B[0] - A[0], B[1] - A[1], B[2] - A[2]);
2963
+ e2.set(C[0] - A[0], C[1] - A[1], C[2] - A[2]);
2964
+ n.crossVectors(e1, e2);
2965
+ const len = n.length();
2966
+ if (len < 1e-12) {
2967
+ next.push(t);
2968
+ return;
2969
+ }
2970
+ n.multiplyScalar(1 / len);
2971
+ ce.set((A[0] + B[0] + C[0]) / 3, (A[1] + B[1] + C[1]) / 3, (A[2] + B[2] + C[2]) / 3);
2972
+ if (T.sel && !T.sel(n, ce)) {
2973
+ next.push(t);
2974
+ return;
2975
+ }
2976
+ const d = [T.sdf(A[0], A[1], A[2]), T.sdf(B[0], B[1], B[2]), T.sdf(C[0], C[1], C[2])];
2977
+ const ins = (d[0] < 0 ? 1 : 0) + (d[1] < 0 ? 1 : 0) + (d[2] < 0 ? 1 : 0);
2978
+ if (ins === 0) next.push(t);
2979
+ else if (ins === 3) {
2980
+ if (rem) rem.push(t);
2981
+ } else {
2982
+ fan(clip(t, d, 1), next);
2983
+ if (rem) fan(clip(t, d, -1), rem);
2984
+ }
2985
+ }
2986
+ const segments = [];
2987
+ let total = 0;
2988
+ const ti = tests.length === 1 && tests[0].bounds ? triIndexOf(geo) : void 0;
2989
+ if (ti) {
2990
+ const T = tests[0];
2991
+ const lo = T.bounds.lo;
2992
+ const hi = T.bounds.hi;
2775
2993
  const rem = T.tag ? cut[T.tag] = cut[T.tag] || [] : null;
2776
- tris.forEach(function(t) {
2777
- const A = t[0];
2778
- const B = t[1];
2779
- const C = t[2];
2780
- e1.set(B[0] - A[0], B[1] - A[1], B[2] - A[2]);
2781
- e2.set(C[0] - A[0], C[1] - A[1], C[2] - A[2]);
2782
- n.crossVectors(e1, e2);
2783
- const len = n.length();
2784
- if (len < 1e-12) {
2785
- next.push(t);
2786
- return;
2787
- }
2788
- n.multiplyScalar(1 / len);
2789
- ce.set((A[0] + B[0] + C[0]) / 3, (A[1] + B[1] + C[1]) / 3, (A[2] + B[2] + C[2]) / 3);
2790
- if (T.sel && !T.sel(n, ce)) {
2791
- next.push(t);
2792
- return;
2994
+ const boxes = ti.boxes;
2995
+ const blocks = ti.blocks;
2996
+ let runFrom = -1;
2997
+ const closeRun = function(end) {
2998
+ if (runFrom < 0) return;
2999
+ segments.push([runFrom, end]);
3000
+ total += end - runFrom;
3001
+ runFrom = -1;
3002
+ };
3003
+ for (let blk = 0, t0 = 0; t0 < ti.triangles; blk++, t0 += TRI_BLOCK) {
3004
+ const j = blk * 6;
3005
+ if (blocks[j + 3] < lo[0] || blocks[j] > hi[0] || blocks[j + 4] < lo[1] || blocks[j + 1] > hi[1] || blocks[j + 5] < lo[2] || blocks[j + 2] > hi[2]) {
3006
+ if (runFrom < 0) runFrom = t0;
3007
+ continue;
2793
3008
  }
2794
- const d = [T.sdf(A[0], A[1], A[2]), T.sdf(B[0], B[1], B[2]), T.sdf(C[0], C[1], C[2])];
2795
- const ins = (d[0] < 0 ? 1 : 0) + (d[1] < 0 ? 1 : 0) + (d[2] < 0 ? 1 : 0);
2796
- if (ins === 0) next.push(t);
2797
- else if (ins === 3) {
2798
- if (rem) rem.push(t);
2799
- } else {
2800
- fan(clip(t, d, 1), next);
2801
- if (rem) fan(clip(t, d, -1), rem);
3009
+ const t1 = Math.min(ti.triangles, t0 + TRI_BLOCK);
3010
+ for (let t = t0; t < t1; t++) {
3011
+ const q = t * 6;
3012
+ if (boxes[q + 3] < lo[0] || boxes[q] > hi[0] || boxes[q + 4] < lo[1] || boxes[q + 1] > hi[1] || boxes[q + 5] < lo[2] || boxes[q + 2] > hi[2]) {
3013
+ if (runFrom < 0) runFrom = t;
3014
+ continue;
3015
+ }
3016
+ closeRun(t);
3017
+ const next = [];
3018
+ testTri(T, readTri(t * 3), next, rem);
3019
+ if (next.length) {
3020
+ segments.push(next);
3021
+ total += next.length;
3022
+ }
2802
3023
  }
3024
+ }
3025
+ closeRun(ti.triangles);
3026
+ } else {
3027
+ let tris = [];
3028
+ for (let f = 0; f < nv; f += 3) tris.push(readTri(f));
3029
+ tests.forEach(function(T) {
3030
+ const next = [];
3031
+ const rem = T.tag ? cut[T.tag] = cut[T.tag] || [] : null;
3032
+ tris.forEach(function(t) {
3033
+ testTri(T, t, next, rem);
3034
+ });
3035
+ tris = next;
2803
3036
  });
2804
- tris = next;
2805
- });
2806
- function build(list2) {
3037
+ segments.push(tris);
3038
+ total = tris.length;
3039
+ }
3040
+ function fill(segs, count, boxesOut2) {
2807
3041
  const g = new THREE.BufferGeometry();
2808
- let off = 0;
2809
- names.forEach(function(nm, a) {
2810
- const arr = new Float32Array(list2.length * 3 * sizes[a]);
2811
- let w = 0;
2812
- list2.forEach(function(t) {
2813
- for (let k = 0; k < 3; k++) for (let q = 0; q < sizes[a]; q++) arr[w++] = t[k][off + q];
3042
+ const dst = names.map(function(_nm, a) {
3043
+ return new Float32Array(count * 3 * sizes[a]);
3044
+ });
3045
+ const w = names.map(function() {
3046
+ return 0;
3047
+ });
3048
+ let wb = 0;
3049
+ segs.forEach(function(seg) {
3050
+ if (typeof seg[0] === "number") {
3051
+ for (let a = 0; a < names.length; a++) {
3052
+ const size = sizes[a];
3053
+ if (!idx) {
3054
+ dst[a].set(arrs[a].subarray(seg[0] * 3 * size, seg[1] * 3 * size), w[a]);
3055
+ w[a] += (seg[1] - seg[0]) * 3 * size;
3056
+ continue;
3057
+ }
3058
+ for (let f = seg[0] * 3; f < seg[1] * 3; f++) for (let q = 0; q < size; q++) dst[a][w[a]++] = arrs[a][idx[f] * size + q];
3059
+ }
3060
+ if (boxesOut2 && ti) {
3061
+ boxesOut2.set(ti.boxes.subarray(seg[0] * 6, seg[1] * 6), wb);
3062
+ wb += (seg[1] - seg[0]) * 6;
3063
+ }
3064
+ return;
3065
+ }
3066
+ seg.forEach(function(t) {
3067
+ if (boxesOut2) {
3068
+ for (let k = 0; k < 3; k++) {
3069
+ boxesOut2[wb + k] = Math.fround(Math.min(t[0][k], t[1][k], t[2][k]));
3070
+ boxesOut2[wb + 3 + k] = Math.fround(Math.max(t[0][k], t[1][k], t[2][k]));
3071
+ }
3072
+ wb += 6;
3073
+ }
3074
+ let off = 0;
3075
+ for (let a = 0; a < names.length; a++) {
3076
+ for (let k = 0; k < 3; k++) for (let q = 0; q < sizes[a]; q++) dst[a][w[a]++] = t[k][off + q];
3077
+ off += sizes[a];
3078
+ }
2814
3079
  });
2815
- g.setAttribute(nm, new THREE.BufferAttribute(arr, sizes[a]));
2816
- off += sizes[a];
3080
+ });
3081
+ names.forEach(function(nm, a) {
3082
+ g.setAttribute(nm, new THREE.BufferAttribute(dst[a], sizes[a]));
2817
3083
  });
2818
3084
  g.userData.crease = true;
2819
3085
  return g;
2820
3086
  }
2821
- const out = { geo: build(tris), cut: {} };
3087
+ const boxesOut = ti ? new Float64Array(total * 6) : void 0;
3088
+ const out = { geo: fill(segments, total, boxesOut), cut: {}, tasks: {} };
3089
+ if (boxesOut) storeTriIndex(out.geo, boxesOut);
2822
3090
  Object.keys(cut).forEach(function(k) {
2823
- out.cut[k] = crease(build(cut[k]), 42);
3091
+ const piece = fill([cut[k]], cut[k].length);
3092
+ const mode = tests.find(function(T) {
3093
+ return T.tag === k;
3094
+ }).pieces;
3095
+ if (mode === "none") out.cut[k] = piece;
3096
+ else if (mode === "later") {
3097
+ out.cut[k] = piece;
3098
+ out.tasks[k] = creaseTask(piece, 42);
3099
+ } else out.cut[k] = crease(piece, 42);
2824
3100
  });
2825
3101
  return out;
2826
3102
  }
3103
+ function refreshBounds(geo) {
3104
+ const found = boundsFromIndex(geo);
3105
+ if (!found) {
3106
+ geo.computeBoundingSphere();
3107
+ geo.computeBoundingBox();
3108
+ return;
3109
+ }
3110
+ if (geo.boundingBox && geo.boundingSphere && boundsDone.get(geo) === found.version) return;
3111
+ const b = found.box;
3112
+ if (!geo.boundingBox) geo.boundingBox = new THREE.Box3();
3113
+ geo.boundingBox.min.set(b[0], b[1], b[2]);
3114
+ geo.boundingBox.max.set(b[3], b[4], b[5]);
3115
+ if (!geo.boundingSphere) geo.boundingSphere = new THREE.Sphere();
3116
+ const center = geo.boundingBox.getCenter(geo.boundingSphere.center);
3117
+ const P = geo.attributes.position.array;
3118
+ let maxRadiusSq = 0;
3119
+ for (let i = 0; i < P.length; i += 3) {
3120
+ const dx = center.x - P[i];
3121
+ const dy = center.y - P[i + 1];
3122
+ const dz = center.z - P[i + 2];
3123
+ maxRadiusSq = Math.max(maxRadiusSq, dx * dx + dy * dy + dz * dz);
3124
+ }
3125
+ geo.boundingSphere.radius = Math.sqrt(maxRadiusSq);
3126
+ boundsDone.set(geo, found.version);
3127
+ }
3128
+ const boundsDone = /* @__PURE__ */ new WeakMap();
2827
3129
  function mergeGeometries(list2) {
2828
3130
  const pos = [];
2829
3131
  const nor = [];
@@ -2954,7 +3256,7 @@ var GameVehicles = (() => {
2954
3256
  }
2955
3257
  };
2956
3258
  };
2957
- Object.assign(I, { roundedBoxGeometry, taper, subdivide, crease, pathShape, tumble, loftGeometry, hullGeometry, cutGeometry, mergeGeometries, uvProject });
3259
+ Object.assign(I, { roundedBoxGeometry, taper, subdivide, crease, creaseTask, refreshBounds, pathShape, tumble, loftGeometry, hullGeometry, cutGeometry, mergeGeometries, uvProject });
2958
3260
  }
2959
3261
 
2960
3262
  // ../game-contracts/dist/index.js
@@ -6359,8 +6661,15 @@ var GameVehicles = (() => {
6359
6661
  x.arc(cx, cy, 26 * s, 0, PI * 2);
6360
6662
  x.fill();
6361
6663
  }
6664
+ const NO_CANVAS = new Proxy(function() {
6665
+ }, { get: () => NO_CANVAS, apply: () => NO_CANVAS, set: () => true });
6362
6666
  function crackGlass(c, msh, p, force, sc) {
6363
6667
  const G = msh.userData.glass;
6668
+ if (G.cracksFreed) {
6669
+ drawWeb(NO_CANVAS, 0, 0, sc != null ? sc : 0.6 + force * 0.5, Math.max(0.4, G.aspect || 1), c.rand);
6670
+ G.hits++;
6671
+ return;
6672
+ }
6364
6673
  if (!G.cv) {
6365
6674
  G.cv = document2.createElement("canvas");
6366
6675
  G.cv.width = G.cv.height = 256;
@@ -6443,6 +6752,14 @@ var GameVehicles = (() => {
6443
6752
  const G = msh.userData.glass;
6444
6753
  if (G.broken) return;
6445
6754
  G.broken = true;
6755
+ const freeCracks = function() {
6756
+ if (!G.tex) return;
6757
+ if (msh.material !== MATERIALS.glassEdge && msh.material?.map === G.tex) msh.material.dispose();
6758
+ G.tex.dispose();
6759
+ G.tex = null;
6760
+ G.cv = null;
6761
+ G.cracksFreed = true;
6762
+ };
6446
6763
  const cubesAt = [];
6447
6764
  if (G.outline && G.T !== void 0) {
6448
6765
  const out = G.outline;
@@ -6476,6 +6793,7 @@ var GameVehicles = (() => {
6476
6793
  geo.computeVertexNormals();
6477
6794
  msh.geometry.dispose();
6478
6795
  msh.geometry = geo;
6796
+ freeCracks();
6479
6797
  msh.material = MATERIALS.glassEdge;
6480
6798
  let minx = 1e9;
6481
6799
  let maxx = -1e9;
@@ -6499,8 +6817,12 @@ var GameVehicles = (() => {
6499
6817
  if (rim) {
6500
6818
  msh.geometry.dispose();
6501
6819
  msh.geometry = rim;
6820
+ freeCracks();
6502
6821
  msh.material = MATERIALS.glassEdge;
6503
- } else msh.visible = false;
6822
+ } else {
6823
+ freeCracks();
6824
+ msh.visible = false;
6825
+ }
6504
6826
  for (let j = 0; j < 36; j++)
6505
6827
  cubesAt.push(
6506
6828
  new V3(
@@ -8335,6 +8657,8 @@ var GameVehicles = (() => {
8335
8657
  const pos = g && g.attributes && g.attributes.position;
8336
8658
  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;
8337
8659
  }
8660
+ let _keep = new Uint32Array(0);
8661
+ let _box = new Float32Array(0);
8338
8662
  function proxyIn(o, Mw, R) {
8339
8663
  _pm.copy(o.matrixWorld).invert().multiply(Mw);
8340
8664
  const lo = [Infinity, Infinity, Infinity];
@@ -8347,39 +8671,48 @@ var GameVehicles = (() => {
8347
8671
  }
8348
8672
  }
8349
8673
  const g = o.geometry;
8350
- const P = g.attributes.position.array;
8674
+ const ti = triIndexOf(g);
8675
+ if (!ti) return null;
8351
8676
  const ix = g.index ? g.index.array : null;
8352
- const n = ix ? ix.length : P.length / 3;
8353
- const keep = new Uint32Array(n - n % 3);
8677
+ const boxes = ti.boxes;
8678
+ const blocks = ti.blocks;
8679
+ if (_keep.length < ti.triangles * 3) {
8680
+ _keep = new Uint32Array(ti.triangles * 3);
8681
+ _box = new Float32Array(ti.triangles * 6);
8682
+ }
8683
+ const keep = _keep;
8684
+ const box2 = _box;
8354
8685
  let nk = 0;
8355
- for (let t = 0; t + 2 < n; t += 3) {
8356
- const a = (ix ? ix[t] : t) * 3;
8357
- const b = (ix ? ix[t + 1] : t + 1) * 3;
8358
- const d = (ix ? ix[t + 2] : t + 2) * 3;
8359
- let out = false;
8360
- for (let k = 0; k < 3 && !out; k++) {
8361
- const u = P[a + k];
8362
- const v = P[b + k];
8363
- const w = P[d + k];
8364
- out = Math.max(u, v, w) < lo[k] || Math.min(u, v, w) > hi[k];
8365
- }
8366
- if (!out) {
8367
- keep[nk++] = ix ? ix[t] : t;
8368
- keep[nk++] = ix ? ix[t + 1] : t + 1;
8369
- keep[nk++] = ix ? ix[t + 2] : t + 2;
8686
+ let nt = 0;
8687
+ for (let blk = 0, t0 = 0; t0 < ti.triangles; blk++, t0 += TRI_BLOCK) {
8688
+ const j = blk * 6;
8689
+ if (blocks[j + 3] < lo[0] || blocks[j] > hi[0] || blocks[j + 4] < lo[1] || blocks[j + 1] > hi[1] || blocks[j + 5] < lo[2] || blocks[j + 2] > hi[2]) continue;
8690
+ const t1 = Math.min(ti.triangles, t0 + TRI_BLOCK);
8691
+ for (let t = t0; t < t1; t++) {
8692
+ const q = t * 6;
8693
+ if (boxes[q + 3] < lo[0] || boxes[q] > hi[0] || boxes[q + 4] < lo[1] || boxes[q + 1] > hi[1] || boxes[q + 5] < lo[2] || boxes[q + 2] > hi[2]) continue;
8694
+ keep[nk++] = ix ? ix[t * 3] : t * 3;
8695
+ keep[nk++] = ix ? ix[t * 3 + 1] : t * 3 + 1;
8696
+ keep[nk++] = ix ? ix[t * 3 + 2] : t * 3 + 2;
8697
+ for (let k = 0; k < 6; k++) box2[nt * 6 + k] = boxes[q + k];
8698
+ nt++;
8370
8699
  }
8371
8700
  }
8372
8701
  if (!nk) return null;
8373
8702
  const kept = keep.slice(0, nk);
8374
- const box2 = new Float32Array(kept.length / 3 * 6);
8375
- for (let t = 0, j = 0; t < kept.length; t += 3, j += 6) {
8376
- const a = kept[t] * 3;
8377
- const b = kept[t + 1] * 3;
8378
- const d = kept[t + 2] * 3;
8703
+ const keptBox = box2.slice(0, nt * 6);
8704
+ const keptBlocks = new Float32Array(Math.ceil(nt / TRI_BLOCK) * 6);
8705
+ for (let blk = 0; blk * TRI_BLOCK < nt; blk++) {
8706
+ const b = blk * 6;
8379
8707
  for (let k = 0; k < 3; k++) {
8380
- box2[j + k] = Math.min(P[a + k], P[b + k], P[d + k]);
8381
- box2[j + 3 + k] = Math.max(P[a + k], P[b + k], P[d + k]);
8708
+ keptBlocks[b + k] = Infinity;
8709
+ keptBlocks[b + 3 + k] = -Infinity;
8382
8710
  }
8711
+ for (let t = blk * TRI_BLOCK, t1 = Math.min(nt, t + TRI_BLOCK); t < t1; t++)
8712
+ for (let k = 0; k < 3; k++) {
8713
+ if (keptBox[t * 6 + k] < keptBlocks[b + k]) keptBlocks[b + k] = keptBox[t * 6 + k];
8714
+ if (keptBox[t * 6 + 3 + k] > keptBlocks[b + 3 + k]) keptBlocks[b + 3 + k] = keptBox[t * 6 + 3 + k];
8715
+ }
8383
8716
  }
8384
8717
  const pg = new THREE.BufferGeometry();
8385
8718
  pg.setAttribute("position", g.attributes.position);
@@ -8390,7 +8723,8 @@ var GameVehicles = (() => {
8390
8723
  proxy.layers.mask = o.layers.mask;
8391
8724
  proxy.proxyOf = o;
8392
8725
  proxy.kept = kept;
8393
- proxy.keptBox = box2;
8726
+ proxy.keptBox = keptBox;
8727
+ proxy.keptBlocks = keptBlocks;
8394
8728
  return proxy;
8395
8729
  }
8396
8730
  function narrowTo(rc, proxy) {
@@ -8410,13 +8744,21 @@ var GameVehicles = (() => {
8410
8744
  const hi2 = Math.max(z0, _pv.z) + 1e-4;
8411
8745
  const K = proxy.kept;
8412
8746
  const X = proxy.keptBox;
8747
+ const KB = proxy.keptBlocks;
8748
+ const nt = K.length / 3;
8413
8749
  const dst = proxy.geometry.index.array;
8414
8750
  let m = 0;
8415
- for (let t = 0, j = 0; t < K.length; t += 3, j += 6) {
8416
- 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;
8417
- dst[m++] = K[t];
8418
- dst[m++] = K[t + 1];
8419
- dst[m++] = K[t + 2];
8751
+ for (let blk = 0, t0 = 0; t0 < nt; blk++, t0 += TRI_BLOCK) {
8752
+ const b = blk * 6;
8753
+ if (KB[b + 3] < lo0 || KB[b] > hi0 || KB[b + 4] < lo1 || KB[b + 1] > hi1 || KB[b + 5] < lo2 || KB[b + 2] > hi2) continue;
8754
+ const t1 = Math.min(nt, t0 + TRI_BLOCK);
8755
+ for (let t = t0; t < t1; t++) {
8756
+ const j = t * 6;
8757
+ 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;
8758
+ dst[m++] = K[t * 3];
8759
+ dst[m++] = K[t * 3 + 1];
8760
+ dst[m++] = K[t * 3 + 2];
8761
+ }
8420
8762
  }
8421
8763
  proxy.geometry.setDrawRange(0, m);
8422
8764
  }
@@ -8834,6 +9176,8 @@ var GameVehicles = (() => {
8834
9176
  irel.copy(rel).invert();
8835
9177
  const pos = g.attributes.position;
8836
9178
  let changed = false;
9179
+ const liveIndex = liveTriIndex(g);
9180
+ const movedVertices = [];
8837
9181
  const near = boxInFrame(B, irel, v);
8838
9182
  for (let i = 0; i < pos.count; i++) {
8839
9183
  v.fromBufferAttribute(pos, i);
@@ -8845,6 +9189,7 @@ var GameVehicles = (() => {
8845
9189
  if (opt.extra) opt.extra(v, f);
8846
9190
  v.applyMatrix4(irel);
8847
9191
  pos.setXYZ(i, v.x, v.y, v.z);
9192
+ movedVertices.push(i);
8848
9193
  changed = true;
8849
9194
  }
8850
9195
  if (opt.extraAll) {
@@ -8853,12 +9198,14 @@ var GameVehicles = (() => {
8853
9198
  if (opt.extraAll(v)) {
8854
9199
  v.applyMatrix4(irel);
8855
9200
  pos.setXYZ(j, v.x, v.y, v.z);
9201
+ movedVertices.push(j);
8856
9202
  changed = true;
8857
9203
  }
8858
9204
  }
8859
9205
  }
8860
9206
  if (changed) {
8861
9207
  pos.needsUpdate = true;
9208
+ if (liveIndex) touchTriIndex(g, liveIndex, movedVertices);
8862
9209
  dirty.add(msh);
8863
9210
  }
8864
9211
  if (changed || boundsDue) {
@@ -8889,6 +9236,26 @@ var GameVehicles = (() => {
8889
9236
  });
8890
9237
  return { field, moved, ax, a, cu, cv, hu, hv, outer };
8891
9238
  }
9239
+ function panelBoundsIn(rel, rect, B, a, ax) {
9240
+ const det = rel.determinant();
9241
+ if (!Number.isFinite(det) || Math.abs(det) < 1e-12) return void 0;
9242
+ const back = rel.clone().invert();
9243
+ const lo = [Infinity, Infinity, Infinity];
9244
+ const hi = [-Infinity, -Infinity, -Infinity];
9245
+ const q = new V3();
9246
+ for (let i = 0; i < 8; i++) {
9247
+ q.setComponent(ax[0], i & 1 ? rect[1] : rect[0]);
9248
+ q.setComponent(ax[1], i & 2 ? rect[3] : rect[2]);
9249
+ q.setComponent(a, i & 4 ? I.boxHi(B, a) : I.boxLo(B, a));
9250
+ q.applyMatrix4(back);
9251
+ for (let k = 0; k < 3; k++) {
9252
+ lo[k] = Math.min(lo[k], q.getComponent(k));
9253
+ hi[k] = Math.max(hi[k], q.getComponent(k));
9254
+ }
9255
+ }
9256
+ const pad = 1e-3;
9257
+ return { lo: lo.map((x) => x - pad), hi: hi.map((x) => x + pad) };
9258
+ }
8892
9259
  function cutPanel(c, z, rect, keep, dirty, o) {
8893
9260
  o = o || {};
8894
9261
  const B = z.box;
@@ -8921,6 +9288,7 @@ var GameVehicles = (() => {
8921
9288
  }
8922
9289
  const test = {
8923
9290
  tag: "part",
9291
+ pieces: keep ? o.pieceTasks ? "later" : "crease" : "none",
8924
9292
  sel: o.anyN || z.cutAll ? null : function(n) {
8925
9293
  nv.copy(n).applyMatrix3(nm).normalize();
8926
9294
  return nv.dot(outN) > 0.3;
@@ -8939,13 +9307,12 @@ var GameVehicles = (() => {
8939
9307
  return q;
8940
9308
  }
8941
9309
  };
8942
- const r = cutGeometry(g, [test]);
9310
+ const r = cutGeometry(g, [{ ...test, bounds: panelBoundsIn(rel, rect, B, a, ax) }]);
8943
9311
  const cg = r.cut.part;
8944
9312
  if (!cg || !cg.attributes.position.count) return;
8945
9313
  g.dispose();
8946
9314
  msh.geometry = r.geo;
8947
- r.geo.computeBoundingSphere();
8948
- r.geo.computeBoundingBox();
9315
+ I.refreshBounds(r.geo);
8949
9316
  dirty.add(msh);
8950
9317
  if (!keep) {
8951
9318
  cg.dispose();
@@ -8958,6 +9325,7 @@ var GameVehicles = (() => {
8958
9325
  rel.decompose(pm.position, pm.quaternion, pm.scale);
8959
9326
  c.vehicle.add(pm);
8960
9327
  pm.updateMatrixWorld(true);
9328
+ if (o.pieceTasks) o.pieceTasks.set(pm, r.tasks.part);
8961
9329
  pieces.push({ mesh: pm, cut: true });
8962
9330
  });
8963
9331
  return pieces;
@@ -12594,7 +12962,15 @@ var GameVehicles = (() => {
12594
12962
  __publicField(this, "gone", /* @__PURE__ */ new Set());
12595
12963
  /** Shows or hides the plate and the recess of a door or lid by its state (see `showWhenOpen`). */
12596
12964
  __publicField(this, "syncPlates", /* @__PURE__ */ new Map());
12965
+ /** Doors and lids whose own skin was cut from the body and hung on the hinge (true), or that had none to take and keep the plate (false). */
12966
+ __publicField(this, "skins", /* @__PURE__ */ new Map());
12597
12967
  __publicField(this, "lightsOn", true);
12968
+ /** Heavy body meshes whose triangle index is still to build (null once done). */
12969
+ __publicField(this, "warming");
12970
+ /** A panel was torn off since the last `pump`: that frame already paid for the cut, the smoothing starts on the next one. */
12971
+ __publicField(this, "tornThisFrame", false);
12972
+ /** Normal smoothing still running, by mesh: a mesh cut again gets a new task and the old one is dropped. */
12973
+ __publicField(this, "smoothing", /* @__PURE__ */ new Map());
12598
12974
  __publicField(this, "hood");
12599
12975
  this.kit = kit;
12600
12976
  this.c = c;
@@ -12604,6 +12980,8 @@ var GameVehicles = (() => {
12604
12980
  for (const p of model.parts) this.byId.set(p.id, p);
12605
12981
  c.root.updateMatrixWorld(true);
12606
12982
  this.hood = this.nameHood();
12983
+ this.warming = c.deform.filter((msh) => msh.geometry && msh.geometry.attributes.position && msh.geometry.attributes.position.count >= 1500);
12984
+ if (!this.warming.length) this.warming = null;
12607
12985
  for (const p of model.parts) {
12608
12986
  if (p.role === "door" || p.role === "lid") this.makePlate(p);
12609
12987
  else if (p.role === "mirror" && !this.mirrorObjects(p).length) this.makeMirror(p);
@@ -12739,11 +13117,21 @@ var GameVehicles = (() => {
12739
13117
  * keeps the shape of its model instead of flat panels over its curved sides. Follows the hinge's rotation, whoever turns it (the clips of a host, `detach`).
12740
13118
  */
12741
13119
  showWhenOpen(id, piv, panel, recess) {
13120
+ let busy = false;
12742
13121
  const sync = () => {
13122
+ if (busy) return;
12743
13123
  const r = piv.rotation;
12744
13124
  const open = Math.abs(r.x) + Math.abs(r.y) + Math.abs(r.z) > 1e-3 || this.gone.has(id) || piv.parent !== this.c.vehicle;
12745
- panel.visible = open;
12746
- recess.visible = open;
13125
+ if (open && !this.gone.has(id) && piv.parent === this.c.vehicle && !this.skins.has(id)) {
13126
+ busy = true;
13127
+ try {
13128
+ this.skins.set(id, this.takeSkin(id, piv));
13129
+ } finally {
13130
+ busy = false;
13131
+ }
13132
+ }
13133
+ panel.visible = open && !this.skins.get(id);
13134
+ recess.visible = open && !this.skins.get(id);
12747
13135
  };
12748
13136
  const euler = piv.rotation;
12749
13137
  const quaternion = piv.quaternion;
@@ -12760,6 +13148,58 @@ var GameVehicles = (() => {
12760
13148
  this.syncPlates.set(id, sync);
12761
13149
  sync();
12762
13150
  }
13151
+ /**
13152
+ * Cuts the skin of a door or lid zone out of the body (as the damage's hanging door does) and hangs it, with the thin parts on it (trim, handle) and, for a door, the side
13153
+ * window over it, on the hinge. Done with the hinge closed, so every piece keeps its place; answers whether there was skin to take.
13154
+ */
13155
+ takeSkin(id, piv) {
13156
+ const p = this.byId.get(id);
13157
+ const z = p && this.zone(p);
13158
+ if (!p || !z) return false;
13159
+ const I = this.I;
13160
+ const B = z.box;
13161
+ const q = piv.quaternion.clone();
13162
+ piv.quaternion.identity();
13163
+ this.c.root.updateMatrixWorld(true);
13164
+ const inv = I.worldInverseOf(this.c);
13165
+ let objs = [];
13166
+ try {
13167
+ if (p.role === "door") {
13168
+ const dirty = /* @__PURE__ */ new Set();
13169
+ const rect = [B.x0 + 0.03, B.x1 - 0.03, B.y0 + Math.min(0.08, (B.y1 - B.y0) * 0.1), B.y1 - 0.02];
13170
+ const pcs = I.cutPanel(this.c, z, rect, true, dirty).map((x) => x.mesh).filter((m) => !!m);
13171
+ this.mend(dirty);
13172
+ if (pcs.length) objs = [...pcs, ...I.followersIn(this.c, z, inv), ...I.windowsOver(this.c, z, inv)];
13173
+ } else {
13174
+ const pcs = this.cutZone(z);
13175
+ if (pcs.length) objs = [...pcs, ...I.followersIn(this.c, z, inv)];
13176
+ }
13177
+ } catch {
13178
+ objs = [];
13179
+ }
13180
+ const deform = this.c.deform ?? [];
13181
+ for (const o of objs) {
13182
+ if (!o.parent || o === piv) continue;
13183
+ piv.attach(o);
13184
+ o.traverse((x) => {
13185
+ x.userData.noFollow = true;
13186
+ });
13187
+ const di = deform.indexOf(o);
13188
+ if (di >= 0) deform.splice(di, 1);
13189
+ }
13190
+ piv.quaternion.copy(q);
13191
+ this.c.root.updateMatrixWorld(true);
13192
+ return objs.length > 0;
13193
+ }
13194
+ mend(dirty) {
13195
+ dirty.forEach((msh) => {
13196
+ const g = msh.geometry;
13197
+ if (g.userData.crease || !g.index) this.I.crease(g, 42);
13198
+ else g.computeVertexNormals();
13199
+ g.computeBoundingSphere();
13200
+ g.computeBoundingBox();
13201
+ });
13202
+ }
12763
13203
  makeMirror(p) {
12764
13204
  const [cx, cy, cz] = p.box.center;
12765
13205
  const s = cz < 0 ? -1 : 1;
@@ -12897,6 +13337,31 @@ var GameVehicles = (() => {
12897
13337
  this.I.flattenTire(w, Math.min(0.5, 0.12 + 0.38 * melt));
12898
13338
  }
12899
13339
  }
13340
+ /** Smooths the normals of a cut mesh a slice per frame (see `pump`); until then it draws with flat face normals. */
13341
+ smoothLater(msh) {
13342
+ this.smoothing.set(msh, this.I.creaseTask(msh.geometry, 42));
13343
+ this.tornThisFrame = true;
13344
+ }
13345
+ /** Spends at most `budgetMs` finishing the pending normal smoothing. Call once per frame. */
13346
+ pump(budgetMs = 2.5) {
13347
+ if (this.tornThisFrame) {
13348
+ this.tornThisFrame = false;
13349
+ return;
13350
+ }
13351
+ if (!this.smoothing.size && !this.warming) return;
13352
+ const deadline = performance.now() + budgetMs;
13353
+ for (const [msh, task] of this.smoothing) {
13354
+ if (msh.geometry !== task.geo || task.step(deadline)) this.smoothing.delete(msh);
13355
+ if (performance.now() >= deadline) return;
13356
+ }
13357
+ this.warmIndexes(deadline);
13358
+ }
13359
+ /** Measures the triangles of the heavy body meshes once, a slice per frame, so the first panel torn off finds them ready (see `triIndexOf`). */
13360
+ warmIndexes(deadline) {
13361
+ if (!this.warming) return;
13362
+ this.warming = this.warming.filter((msh) => !warmTriIndex(msh.geometry, deadline));
13363
+ if (!this.warming.length) this.warming = null;
13364
+ }
12900
13365
  /** Moves the panel of a lost zone out of the body (the kit's torn-off look). */
12901
13366
  tearOff(p) {
12902
13367
  const z = this.zone(p);
@@ -12909,10 +13374,9 @@ var GameVehicles = (() => {
12909
13374
  }
12910
13375
  dirty.forEach((msh) => {
12911
13376
  const g = msh.geometry;
12912
- if (g.userData.crease || !g.index) this.I.crease(g, 42);
13377
+ if (g.userData.crease || !g.index) this.smoothLater(msh);
12913
13378
  else g.computeVertexNormals();
12914
- g.computeBoundingSphere();
12915
- g.computeBoundingBox();
13379
+ this.I.refreshBounds(g);
12916
13380
  });
12917
13381
  this.gone.add(p.id);
12918
13382
  }
@@ -13055,17 +13519,19 @@ var GameVehicles = (() => {
13055
13519
  const mg = Math.min(0.05, (u1 - u0) * 0.08, (v1 - v0) * 0.08);
13056
13520
  const dirty = /* @__PURE__ */ new Set();
13057
13521
  let pcs = [];
13522
+ const pieceTasks = /* @__PURE__ */ new Map();
13058
13523
  try {
13059
- pcs = I.cutPanel(this.c, z, [u0 + mg, u1 - mg, v0 + mg, v1 - mg], true, dirty);
13524
+ pcs = I.cutPanel(this.c, z, [u0 + mg, u1 - mg, v0 + mg, v1 - mg], true, dirty, { pieceTasks });
13060
13525
  } catch {
13061
13526
  pcs = [];
13062
13527
  }
13528
+ pieceTasks.forEach((task, mesh2) => this.smoothing.set(mesh2, task));
13529
+ this.tornThisFrame = true;
13063
13530
  dirty.forEach((msh) => {
13064
13531
  const g = msh.geometry;
13065
- if (g.userData.crease || !g.index) I.crease(g, 42);
13532
+ if (g.userData.crease || !g.index) this.smoothLater(msh);
13066
13533
  else g.computeVertexNormals();
13067
- g.computeBoundingSphere();
13068
- g.computeBoundingBox();
13534
+ I.refreshBounds(g);
13069
13535
  });
13070
13536
  return pcs.map((q) => q.mesh).filter((m) => !!m);
13071
13537
  }
@@ -13844,7 +14310,7 @@ var GameVehicles = (() => {
13844
14310
  specCache = { name: options.name ?? c.spec.name ?? c.key, parts, joints, tags: ["vehicle", kindOf(c)] };
13845
14311
  return specCache;
13846
14312
  };
13847
- const state = () => {
14313
+ const buildState = () => {
13848
14314
  const parts = model.parts.map((d) => {
13849
14315
  const p = ps.get(d.id);
13850
14316
  const marked = p.dents.length > 0 || p.holes.length > 0 || p.cracks.length > 0;
@@ -13881,7 +14347,15 @@ var GameVehicles = (() => {
13881
14347
  }));
13882
14348
  return { id, name: options.name ?? c.spec.name ?? c.key, position: [0, 0, 0], rotation: [0, 0, 0, 1], parts, joints, groups: groupsCache.map((g) => [...g]), destroyed: parts.every((p) => p.status === "broken" || p.status === "melted" || p.status === "ashes") };
13883
14349
  };
14350
+ let stateCache;
14351
+ const state = () => stateCache ?? (stateCache = buildState());
14352
+ const isSimulating = () => {
14353
+ if (leaks.size > 0) return true;
14354
+ for (const p of ps.values()) if (!p.lost && (p.burning || p.temp > AMBIENT)) return true;
14355
+ return false;
14356
+ };
13884
14357
  const hit = (h) => {
14358
+ stateCache = void 0;
13885
14359
  const out = [];
13886
14360
  const hh = h.space === "local" ? { ...h, space: "world", point: toWorld(h.point) } : h;
13887
14361
  if (hh.space === "local" || !hh.point) return out;
@@ -13908,6 +14382,8 @@ var GameVehicles = (() => {
13908
14382
  clock += dt;
13909
14383
  const out = queue;
13910
14384
  queue = [];
14385
+ const wasSimulating = isSimulating();
14386
+ const handledBefore = (blastHandled ? 1 : 0) + (rollHandled ? 2 : 0);
13911
14387
  const hood = c.hood;
13912
14388
  if (hood) {
13913
14389
  const sign2 = hood.sign || 1;
@@ -13928,12 +14404,15 @@ var GameVehicles = (() => {
13928
14404
  afterBlast(out);
13929
14405
  afterRoll(out);
13930
14406
  applyEffects();
14407
+ vis?.pump();
14408
+ if (wasSimulating || out.length > 0 || handledBefore !== (blastHandled ? 1 : 0) + (rollHandled ? 2 : 0)) stateCache = void 0;
13931
14409
  return out;
13932
14410
  };
13933
14411
  const disassemble = (jointId, tool, dt) => {
13934
14412
  const j = js.find((x) => x.def.id === jointId);
13935
14413
  if (!j || !j.def.tools.includes(tool)) return -1;
13936
14414
  if (j.open) return 1;
14415
+ stateCache = void 0;
13937
14416
  j.progress = Math.min(1, j.progress + dt / j.def.seconds);
13938
14417
  const step = Math.floor(j.progress * 4) / 4;
13939
14418
  if (step > j.reported && j.progress < 1) {
@@ -13949,6 +14428,7 @@ var GameVehicles = (() => {
13949
14428
  return j.progress;
13950
14429
  };
13951
14430
  const extinguish = (partId) => {
14431
+ stateCache = void 0;
13952
14432
  for (const p of ps.values()) {
13953
14433
  if (partId && p.def.id !== partId) continue;
13954
14434
  if (p.burning) {
@@ -13962,6 +14442,7 @@ var GameVehicles = (() => {
13962
14442
  }
13963
14443
  };
13964
14444
  const reset = () => {
14445
+ stateCache = void 0;
13965
14446
  const old = c;
13966
14447
  const parent = old.root.parent;
13967
14448
  const next = kit.build(old.key, old.state, old.scenario != null ? { scenario: old.scenario } : void 0);
@@ -13979,6 +14460,7 @@ var GameVehicles = (() => {
13979
14460
  setup();
13980
14461
  };
13981
14462
  const rollover = (side2 = 1) => {
14463
+ stateCache = void 0;
13982
14464
  if (kit.roll(c, side2)) rolled = true;
13983
14465
  void rolled;
13984
14466
  };