@adatechnology/game-weapons-three 0.10.4 → 0.11.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.
- package/README.md +2 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.global.js +828 -206
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +153 -46
- package/dist/index.js.map +1 -1
- package/package.json +12 -13
package/dist/index.global.js
CHANGED
|
@@ -19264,26 +19264,26 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19264
19264
|
for (const e of planned.items) if (e.type === "wall_switch") e.lit = (e.connections.controls || []).some((id) => (planned.items.find((x) => x.id === id) || {}).lit === true);
|
|
19265
19265
|
}
|
|
19266
19266
|
for (const e of planned.items) if (e.type === "lantern_lamp") e.item.on = !!e.lit;
|
|
19267
|
-
let
|
|
19267
|
+
let scratch2 = null;
|
|
19268
19268
|
function probe(e, hinge, setback) {
|
|
19269
|
-
if (!
|
|
19269
|
+
if (!scratch2) scratch2 = GI.createWorld({ locale, strings: hh.strings, items: hh.items, loot: hh.loot, grid: false });
|
|
19270
19270
|
const it = e.item;
|
|
19271
19271
|
const x = it.x + Math.sin(it.ry) * setback;
|
|
19272
19272
|
const z = it.z + Math.cos(it.ry) * setback;
|
|
19273
19273
|
const spec = hh.build(e.type, Object.assign({}, it, { hinge }), HH.createRecorder().api);
|
|
19274
|
-
|
|
19274
|
+
scratch2.add("__probe", spec, { x, y: e.floorY, z, ry: it.ry });
|
|
19275
19275
|
const blocks = (px, pz) => isBlocked2(px, pz, e.floor);
|
|
19276
19276
|
let sum2 = 0;
|
|
19277
19277
|
let min2 = 1;
|
|
19278
19278
|
let n = 0;
|
|
19279
19279
|
for (const p of spec.parts || []) {
|
|
19280
19280
|
if (p.motion.kind !== "hinge" || !(p.bounds || p.sweep)) continue;
|
|
19281
|
-
const t =
|
|
19281
|
+
const t = scratch2.fitSweep("__probe", p.id, blocks);
|
|
19282
19282
|
sum2 += t;
|
|
19283
19283
|
min2 = Math.min(min2, t);
|
|
19284
19284
|
n++;
|
|
19285
19285
|
}
|
|
19286
|
-
|
|
19286
|
+
scratch2.remove("__probe");
|
|
19287
19287
|
return { sum: sum2, min: min2, n };
|
|
19288
19288
|
}
|
|
19289
19289
|
const better = (a, b) => a.sum > b.sum + 1e-9 || Math.abs(a.sum - b.sum) <= 1e-9 && a.min > b.min + 1e-9;
|
|
@@ -21972,6 +21972,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
21972
21972
|
var FLAMMABLE_FLUIDS = ["gas", "fuel"];
|
|
21973
21973
|
var VENT_DIAMETER = 8e-3;
|
|
21974
21974
|
var FLAME_LIGHTS_LEAK_C = 470;
|
|
21975
|
+
var RESTING_HOLD_COS = Math.cos(Math.PI / 6);
|
|
21975
21976
|
var BREACH_RADIUS = 0.55;
|
|
21976
21977
|
var BREACH_BLAST_FACTOR = 6;
|
|
21977
21978
|
var BREACH_AREA = 0.25;
|
|
@@ -21987,6 +21988,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
21987
21988
|
var BREACH_GRID_CAP = 4096;
|
|
21988
21989
|
var CHIP_ENERGY = 200;
|
|
21989
21990
|
var CHIP_SPLASH = 0.5;
|
|
21991
|
+
var CRATER_SPENT = 0.6;
|
|
21990
21992
|
var BAR_BOW = 0.08;
|
|
21991
21993
|
var BULLET_JOINT_SPAN = 0.3;
|
|
21992
21994
|
var CUT_SLICE = 1;
|
|
@@ -22007,12 +22009,22 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
22007
22009
|
const cells = p.st.chipped;
|
|
22008
22010
|
if (!cells?.length) return 0;
|
|
22009
22011
|
const [u, v] = planeAxes(p.thin);
|
|
22010
|
-
const
|
|
22012
|
+
const role = p.spec.structure?.role;
|
|
22013
|
+
const up = rotateVec(p.q, [0, 0, 0].map((_, k) => k === v ? 1 : 0));
|
|
22014
|
+
const upright = Math.abs(up[1]) > 0.7 ? v : Math.abs(rotateVec(p.q, [0, 0, 0].map((_, k) => k === u ? 1 : 0))[1]) > 0.7 ? u : -1;
|
|
22015
|
+
const along = (role === "wall" || role === "partition" || role === "column") && upright >= 0 ? upright : p.dims[u] >= p.dims[v] ? u : v;
|
|
22011
22016
|
const across2 = along === u ? v : u;
|
|
22012
22017
|
let worst = 0;
|
|
22013
22018
|
for (const c of cells) {
|
|
22014
|
-
|
|
22015
|
-
for (const d of cells) if (Math.abs(d.center[along] - c.center[along]) < d.size[along] / 2 + 1e-6)
|
|
22019
|
+
const spans = [];
|
|
22020
|
+
for (const d of cells) if (Math.abs(d.center[along] - c.center[along]) < d.size[along] / 2 + 1e-6) spans.push([d.center[across2] - d.size[across2] / 2, d.center[across2] + d.size[across2] / 2]);
|
|
22021
|
+
spans.sort((a, b) => a[0] - b[0]);
|
|
22022
|
+
let gone = 0, end3 = -Infinity;
|
|
22023
|
+
for (const [a, b] of spans) {
|
|
22024
|
+
if (b <= end3) continue;
|
|
22025
|
+
gone += b - Math.max(a, end3);
|
|
22026
|
+
end3 = b;
|
|
22027
|
+
}
|
|
22016
22028
|
worst = Math.max(worst, gone / Math.max(p.dims[across2], 1e-6));
|
|
22017
22029
|
}
|
|
22018
22030
|
return Math.min(1, worst);
|
|
@@ -22308,7 +22320,18 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
22308
22320
|
if (k < 0 || j.spec.kind !== "tie") return k;
|
|
22309
22321
|
return partXf(o, o.parts[k]).c[1] > partXf(o, o.parts[r]).c[1] + 1e-6 ? k : -1;
|
|
22310
22322
|
};
|
|
22323
|
+
let poseVersion = 0;
|
|
22324
|
+
const contactMemo = /* @__PURE__ */ new WeakMap();
|
|
22311
22325
|
const contactBox = (p, support) => {
|
|
22326
|
+
let byTouching = contactMemo.get(p);
|
|
22327
|
+
if (!byTouching) contactMemo.set(p, byTouching = /* @__PURE__ */ new Map());
|
|
22328
|
+
const known = byTouching.get(support);
|
|
22329
|
+
if (known && known.version === poseVersion) return known.box;
|
|
22330
|
+
const box5 = measureContact(p, support);
|
|
22331
|
+
byTouching.set(support, { version: poseVersion, box: box5 });
|
|
22332
|
+
return box5;
|
|
22333
|
+
};
|
|
22334
|
+
const measureContact = (p, support) => {
|
|
22312
22335
|
const A = aabbOf(p.spec.shape, p.pos, p.q), B = aabbOf(support.spec.shape, support.pos, support.q);
|
|
22313
22336
|
const lo = [0, 0, 0], hi = [0, 0, 0];
|
|
22314
22337
|
for (let k = 0; k < 3; k++) {
|
|
@@ -23103,7 +23126,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23103
23126
|
p.st.holes.push(hole);
|
|
23104
23127
|
if (p.st.holes.length > maxHoles) p.st.holes.shift();
|
|
23105
23128
|
markDamaged(p);
|
|
23106
|
-
emit2({ ...base(o, src), type: "hole", part: p.spec.id, hole, worldPoint: wp, ...exit ? { exitPoint: exit } : {} });
|
|
23129
|
+
emit2({ ...base(o, src), type: "hole", part: p.spec.id, hole, worldPoint: wp, ...exit ? { exitPoint: exit } : {}, ...p.spec.powder ? { powder: { ...p.spec.powder } } : {} });
|
|
23107
23130
|
setStage(o, p, 1, {}, src);
|
|
23108
23131
|
return hole;
|
|
23109
23132
|
};
|
|
@@ -23120,6 +23143,22 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23120
23143
|
const detachEvents = (o, opened, by, src) => {
|
|
23121
23144
|
for (const j of opened) emit2({ ...base(o, src), type: "detach", joint: j.spec.id, parts: [j.spec.a, j.spec.b], by, groups: o.st.groups.map((g) => [...g]) });
|
|
23122
23145
|
};
|
|
23146
|
+
const slideOffResting = (o, rotation) => {
|
|
23147
|
+
const upY = 1 - 2 * (rotation[0] * rotation[0] + rotation[2] * rotation[2]);
|
|
23148
|
+
if (upY >= RESTING_HOLD_COS) return;
|
|
23149
|
+
const opened = o.joints.filter((j) => j.spec.kind === "resting" && !j.st.open);
|
|
23150
|
+
if (opened.length === 0) return;
|
|
23151
|
+
run(() => {
|
|
23152
|
+
for (const j of opened) {
|
|
23153
|
+
j.st.open = true;
|
|
23154
|
+
j.st.openedBy = "break";
|
|
23155
|
+
}
|
|
23156
|
+
solveStructure(o);
|
|
23157
|
+
recomputeGroups(o);
|
|
23158
|
+
detachEvents(o, opened, "break");
|
|
23159
|
+
collapseCheck(o);
|
|
23160
|
+
});
|
|
23161
|
+
};
|
|
23123
23162
|
const checkDestroyed = (o, src) => {
|
|
23124
23163
|
if (!o.st.destroyed && o.parts.length > 0 && o.parts.every(isLost)) {
|
|
23125
23164
|
o.st.destroyed = true;
|
|
@@ -23220,7 +23259,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23220
23259
|
const localHolesOf = (p) => p.st.holes.map((h) => ({ point: toLocal(p.pos, p.q, h.point), normal: rotateVec(quat.conjugate(p.q), h.normal), diameter: h.diameter }));
|
|
23221
23260
|
const unitsOf2 = (p) => p.spec.units ?? { size: [p.mat.fragmentSize?.[1] ?? 0.2, p.mat.fragmentSize?.[1] ?? 0.2, p.thickness], bond: "stack" };
|
|
23222
23261
|
const FRAGMENT_SHAPE = { shatter: "shard", splinter: "splinter", tear: "scrap" };
|
|
23223
|
-
const cellFragments = (o, p, cells, hitW, dir, speed, mass, tag) => {
|
|
23262
|
+
const cellFragments = (o, p, cells, hitW, dir, speed, mass, tag, inner = false) => {
|
|
23224
23263
|
const { c, q } = partXf(o, p);
|
|
23225
23264
|
const holes = p.st.holes.length > 0 ? localHolesOf(p) : [];
|
|
23226
23265
|
const [u, v] = planeAxes(p.thin);
|
|
@@ -23244,7 +23283,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23244
23283
|
velocity: [dv[0] * k, dv[1] * k + 0.5 * rnd2(), dv[2] * k],
|
|
23245
23284
|
angularVelocity: [(rnd2() - 0.5) * 8, (rnd2() - 0.5) * 8, (rnd2() - 0.5) * 8],
|
|
23246
23285
|
mass: mass * faceArea(cell) / total,
|
|
23247
|
-
...marks.length > 0 ? { marks } : {}
|
|
23286
|
+
...marks.length > 0 ? { marks } : {},
|
|
23287
|
+
...inner ? { inner } : {}
|
|
23248
23288
|
};
|
|
23249
23289
|
});
|
|
23250
23290
|
};
|
|
@@ -23285,7 +23325,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23285
23325
|
return cells.some((c) => Math.abs(lp[u] - c.center[u]) <= c.size[u] / 2 && Math.abs(lp[v] - c.center[v]) <= c.size[v] / 2);
|
|
23286
23326
|
};
|
|
23287
23327
|
const chipUnits = (o, p, kind, wp, hitEnergy, dir, radius, src) => {
|
|
23288
|
-
var _a, _b;
|
|
23328
|
+
var _a, _b, _c;
|
|
23289
23329
|
if (!p.spec.units || !p.staged || chipEnergy <= 0 || p.st.stripped || isLost(p) || (p.st.stage ?? 0) >= 2) return;
|
|
23290
23330
|
const rule = p.mat.hits?.[kind];
|
|
23291
23331
|
const perKg = rule?.unitJoules ?? chipEnergy;
|
|
@@ -23295,7 +23335,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23295
23335
|
const lp = toLocal(p.pos, p.q, objLocal(o, wp));
|
|
23296
23336
|
const grid = gridOf(p);
|
|
23297
23337
|
const worn = (_a = p.rt).chips ?? (_a.chips = {});
|
|
23338
|
+
const cut = (_b = p.rt).craters ?? (_b.craters = {});
|
|
23298
23339
|
const out = [];
|
|
23340
|
+
const shot = kind === "ballistic";
|
|
23299
23341
|
for (let i = 0; i < grid.length; i++) {
|
|
23300
23342
|
const cell = grid[i];
|
|
23301
23343
|
if (worn[i] === -1) continue;
|
|
@@ -23308,18 +23350,39 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23308
23350
|
worn[i] = total;
|
|
23309
23351
|
continue;
|
|
23310
23352
|
}
|
|
23311
|
-
|
|
23312
|
-
|
|
23353
|
+
if (!shot) {
|
|
23354
|
+
worn[i] = -1;
|
|
23355
|
+
out.push(cell);
|
|
23356
|
+
continue;
|
|
23357
|
+
}
|
|
23358
|
+
const size = [cell.size[0], cell.size[1], cell.size[2]];
|
|
23359
|
+
const center = [cell.center[0], cell.center[1], cell.center[2]];
|
|
23360
|
+
for (const a of [u, v]) {
|
|
23361
|
+
const w = Math.min(cell.size[a], craterAcross(hitEnergy) * (0.85 + 0.3 * rnd2()));
|
|
23362
|
+
const lo = cell.center[a] - cell.size[a] / 2 + w / 2, hi = cell.center[a] + cell.size[a] / 2 - w / 2;
|
|
23363
|
+
size[a] = w;
|
|
23364
|
+
center[a] = clamp2(lp[a], lo, hi);
|
|
23365
|
+
}
|
|
23366
|
+
const taken2 = (cut[i] ?? 0) + size[u] * size[v];
|
|
23367
|
+
if (taken2 >= cell.size[u] * cell.size[v] * CRATER_SPENT) {
|
|
23368
|
+
worn[i] = -1;
|
|
23369
|
+
out.push({ ...cell, crater: true });
|
|
23370
|
+
continue;
|
|
23371
|
+
}
|
|
23372
|
+
worn[i] = 0;
|
|
23373
|
+
cut[i] = taken2;
|
|
23374
|
+
out.push({ center, size, crater: true });
|
|
23313
23375
|
}
|
|
23314
23376
|
if (out.length === 0) return;
|
|
23315
23377
|
const { c, q } = partXf(o, p);
|
|
23316
|
-
const chipped = (
|
|
23378
|
+
const chipped = (_c = p.st).chipped ?? (_c.chipped = []);
|
|
23317
23379
|
const holes = localHolesOf(p);
|
|
23318
23380
|
for (const cell of out) {
|
|
23319
23381
|
const mass = Math.min(p.st.mass, p.mat.density * cell.size[0] * cell.size[1] * cell.size[2]);
|
|
23320
23382
|
const center = toWorld(c, q, cell.center);
|
|
23321
23383
|
const speed = clamp2(Math.sqrt(2 * energy * 0.05 / Math.max(mass, 0.05)), 0.5, 6);
|
|
23322
|
-
const
|
|
23384
|
+
const tag = `c${chipped.length}_`;
|
|
23385
|
+
const fragments = cell.crater ? craterFragments(o, p, cell, dir, speed, mass, tag) : cellFragments(o, p, [cell], wp, dir, speed, mass, tag, true);
|
|
23323
23386
|
p.st.holes = p.st.holes.filter((_, k) => marksInBox(cell.center, cell.size, void 0, [holes[k]]).length === 0);
|
|
23324
23387
|
chipped.push({ center: [cell.center[0], cell.center[1], cell.center[2]], size: [cell.size[0], cell.size[1], cell.size[2]] });
|
|
23325
23388
|
p.st.mass = Math.max(0, p.st.mass - mass);
|
|
@@ -23336,6 +23399,44 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23336
23399
|
collapseCheck(o, src);
|
|
23337
23400
|
}
|
|
23338
23401
|
};
|
|
23402
|
+
const craterAcross = (energy) => clamp2(0.03 + 12e-4 * Math.sqrt(Math.max(0, energy)), 0.03, 0.14);
|
|
23403
|
+
const craterFragments = (o, p, cell, dir, speed, mass, tag) => {
|
|
23404
|
+
const { c, q } = partXf(o, p);
|
|
23405
|
+
const [u, v] = planeAxes(p.thin);
|
|
23406
|
+
const d = dir ?? rotateVec(q, [p.thin === 0 ? 1 : 0, p.thin === 1 ? 1 : 0, p.thin === 2 ? 1 : 0]);
|
|
23407
|
+
const exitSign = Math.sign(rotateVec(quat.conjugate(q), d)[p.thin]) || 1;
|
|
23408
|
+
const bit = Math.max(0.01, Math.min(cell.size[u], cell.size[v]) / 3);
|
|
23409
|
+
const n = clamp2(Math.round(cell.size[u] * cell.size[v] / (bit * bit * 3)), 4, 10) + Math.floor(rnd2() * 3);
|
|
23410
|
+
const out = [];
|
|
23411
|
+
for (let i = 0; i < n; i++) {
|
|
23412
|
+
const back = i % 3 === 2;
|
|
23413
|
+
const lc = [cell.center[0], cell.center[1], cell.center[2]];
|
|
23414
|
+
lc[u] += (rnd2() - 0.5) * cell.size[u] * 0.7;
|
|
23415
|
+
lc[v] += (rnd2() - 0.5) * cell.size[v] * 0.7;
|
|
23416
|
+
lc[p.thin] += (back ? -exitSign : exitSign) * (p.thickness / 2 + 0.01);
|
|
23417
|
+
const size = [0, 0, 0];
|
|
23418
|
+
size[u] = bit * (0.6 + 0.8 * rnd2());
|
|
23419
|
+
size[v] = bit * (0.5 + 0.7 * rnd2());
|
|
23420
|
+
size[p.thin] = bit * (0.4 + 0.6 * rnd2());
|
|
23421
|
+
const spread3 = [(rnd2() - 0.5) * 0.9, (rnd2() - 0.3) * 0.9, (rnd2() - 0.5) * 0.9];
|
|
23422
|
+
const k = speed * (back ? 0.4 + 0.4 * rnd2() : 0.7 + 0.8 * rnd2());
|
|
23423
|
+
const vdir = vec.normalize(vec.add(back ? vec.scale(d, -1) : d, spread3));
|
|
23424
|
+
out.push({
|
|
23425
|
+
id: `${p.spec.id}#${tag}${i}`,
|
|
23426
|
+
part: p.spec.id,
|
|
23427
|
+
material: p.spec.material,
|
|
23428
|
+
shape: "chunk",
|
|
23429
|
+
center: toWorld(c, q, lc),
|
|
23430
|
+
size,
|
|
23431
|
+
rotation: q,
|
|
23432
|
+
velocity: [vdir[0] * k, vdir[1] * k + 0.5 * rnd2(), vdir[2] * k],
|
|
23433
|
+
angularVelocity: [(rnd2() - 0.5) * 20, (rnd2() - 0.5) * 20, (rnd2() - 0.5) * 20],
|
|
23434
|
+
mass: mass / n,
|
|
23435
|
+
inner: true
|
|
23436
|
+
});
|
|
23437
|
+
}
|
|
23438
|
+
return out;
|
|
23439
|
+
};
|
|
23339
23440
|
const exposeRebar = (o, p, src) => {
|
|
23340
23441
|
const r = p.spec.reinforcement;
|
|
23341
23442
|
if (!r) return;
|
|
@@ -23623,7 +23724,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23623
23724
|
});
|
|
23624
23725
|
}
|
|
23625
23726
|
const patch = [scale * 4, scale * 4, scale * 4];
|
|
23626
|
-
emit2({ ...base(o, src), type: "chip", part: p.spec.id, center: exit ?? entry2, size: patch, rotation: q, fragments });
|
|
23727
|
+
emit2({ ...base(o, src), type: "chip", part: p.spec.id, center: exit ?? entry2, size: patch, rotation: q, fragments, torn: true });
|
|
23627
23728
|
};
|
|
23628
23729
|
const ballistic = (o, p, entry2, n, d, E, caliber, pathLen, src, round5 = {}) => {
|
|
23629
23730
|
const mat = p.mat;
|
|
@@ -23682,12 +23783,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23682
23783
|
} else if (mat.ductility >= 0.5) {
|
|
23683
23784
|
addDent(o, p, entry2, n, Math.max(caliber * 3 / 1e3, 0.012), clamp2(Math.min(Math.max(p.thickness * 3, 3e-3), ratio * 0.01), 1e-3, 0.05), src);
|
|
23684
23785
|
} else if (brittle) {
|
|
23685
|
-
addCrack(o, p, entry2, n, crackLen, src);
|
|
23786
|
+
if (!p.spec.units) addCrack(o, p, entry2, n, crackLen, src);
|
|
23686
23787
|
if (mat.fracture !== "shatter") addHole(o, p, entry2, n, caliber / 1e3, false, void 0, src);
|
|
23687
23788
|
} else addHole(o, p, entry2, n, caliber / 1e3, false, void 0, src);
|
|
23688
23789
|
debris2(o, p, mat.hitDebris, entry2, n, clamp2(Math.round(E / 60) + 2, 2, 14), clamp2(Math.sqrt(E) / 6, 1, 8), src);
|
|
23689
23790
|
wearJoints(o, p, (penetrated ? thr : E) * 0.02, void 0, 0, src, true, BULLET_JOINT_SPAN);
|
|
23690
|
-
const
|
|
23791
|
+
const hollowTear = mat.fracture === "tear" && p.spec.fill !== void 0 && p.spec.fill < 0.5;
|
|
23792
|
+
const absorbed = penetrated && (mat.ductility >= 0.5 && mat.fracture !== "tear" || hollowTear) ? Math.min(E, thr) : E;
|
|
23691
23793
|
const dmg = applyDamage(o, p, absorbed * DMG_PER_J * scale, "ballistic", entry2, d, absorbed * 0.1, 12, src);
|
|
23692
23794
|
if (!isLost(p)) tearAt(o, p, "ballistic", entry2, penetrated ? exitPoint : void 0, d, E, caliber / 1e3, src);
|
|
23693
23795
|
chipUnits(o, p, "ballistic", entry2, penetrated ? Math.min(E, Math.max(thr, E * 0.5)) : E, d, Math.max(0.05, caliber / 1e3 * 8), src);
|
|
@@ -23772,10 +23874,25 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23772
23874
|
if (dist2 <= BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3)) addBreach(o, p, wp, breachAreaOf(p, dealt, true), Ein, null, src);
|
|
23773
23875
|
return reach;
|
|
23774
23876
|
};
|
|
23877
|
+
const blastProbe = { partsTested: 0, partsNear: 0 };
|
|
23878
|
+
const blastCandidates = (o, point, radius) => {
|
|
23879
|
+
const local2 = toLocal(o.st.position, o.st.rotation, point);
|
|
23880
|
+
const near = [];
|
|
23881
|
+
for (const p of o.parts) {
|
|
23882
|
+
const reach = radius + p.bound;
|
|
23883
|
+
const dx = local2[0] - p.pos[0];
|
|
23884
|
+
const dy = local2[1] - p.pos[1];
|
|
23885
|
+
const dz = local2[2] - p.pos[2];
|
|
23886
|
+
if (dx * dx + dy * dy + dz * dz < reach * reach) near.push(p);
|
|
23887
|
+
}
|
|
23888
|
+
blastProbe.partsTested += o.parts.length;
|
|
23889
|
+
blastProbe.partsNear += near.length;
|
|
23890
|
+
return near;
|
|
23891
|
+
};
|
|
23775
23892
|
const blastObject = (o, point, energy, radius, src) => {
|
|
23776
23893
|
const start = cur.length;
|
|
23777
23894
|
const res = { object: o.id, damage: 0, penetrated: false, exitEnergy: 0, events: [] };
|
|
23778
|
-
for (const p of o
|
|
23895
|
+
for (const p of blastCandidates(o, point, radius)) blastPart(o, p, point, energy, radius, src, res);
|
|
23779
23896
|
res.events = cur.slice(start);
|
|
23780
23897
|
return res;
|
|
23781
23898
|
};
|
|
@@ -23899,7 +24016,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23899
24016
|
let order = 0;
|
|
23900
24017
|
for (const o of inRange) {
|
|
23901
24018
|
if (only && only !== o) continue;
|
|
23902
|
-
for (const p of o
|
|
24019
|
+
for (const p of blastCandidates(o, point, radius)) {
|
|
23903
24020
|
if (isLost(p)) continue;
|
|
23904
24021
|
const dist2 = distToPart(o, p, point);
|
|
23905
24022
|
if (dist2 < radius) items.push({ dist: dist2, order: order++, o, p });
|
|
@@ -24446,6 +24563,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
24446
24563
|
const p = getPart(o, partId);
|
|
24447
24564
|
p.pos = [position[0], position[1], position[2]];
|
|
24448
24565
|
p.q = q;
|
|
24566
|
+
poseVersion++;
|
|
24449
24567
|
updateBound(o);
|
|
24450
24568
|
},
|
|
24451
24569
|
remove(id) {
|
|
@@ -24464,6 +24582,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
24464
24582
|
const o = getObj(id);
|
|
24465
24583
|
o.base = { pos: [position[0], position[1], position[2]], rot: [rotation[0], rotation[1], rotation[2], rotation[3]] };
|
|
24466
24584
|
applySoilPose(o);
|
|
24585
|
+
slideOffResting(o, rotation);
|
|
24467
24586
|
},
|
|
24468
24587
|
splitGroup(id, group, newId) {
|
|
24469
24588
|
if (impls.has(id)) throw new Error(`object "${id}" is a registered DestructibleImpl and cannot be split; its module owns its pieces`);
|
|
@@ -24487,7 +24606,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
24487
24606
|
if (o.spec.name !== void 0) s.name = o.spec.name;
|
|
24488
24607
|
if (o.spec.tags) s.tags = [...o.spec.tags];
|
|
24489
24608
|
if (o.spec.soil !== void 0) s.soil = o.spec.soil;
|
|
24490
|
-
return
|
|
24609
|
+
return s;
|
|
24491
24610
|
};
|
|
24492
24611
|
const inNew = (pid) => moved.has(pid);
|
|
24493
24612
|
const inOld = (pid) => !moved.has(pid);
|
|
@@ -24514,7 +24633,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
24514
24633
|
const p = getPart(o, partId);
|
|
24515
24634
|
return whole(() => doHit(o, h, p));
|
|
24516
24635
|
},
|
|
24636
|
+
blastProbe() {
|
|
24637
|
+
return { ...blastProbe };
|
|
24638
|
+
},
|
|
24517
24639
|
explode(point, energy, radius, source, blast) {
|
|
24640
|
+
blastProbe.partsTested = 0;
|
|
24641
|
+
blastProbe.partsNear = 0;
|
|
24518
24642
|
return withCollapses(run(() => blastWorld(point, energy, radius, source, blast)));
|
|
24519
24643
|
},
|
|
24520
24644
|
raycast(origin, direction, maxDistance = Infinity) {
|
|
@@ -24524,7 +24648,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
24524
24648
|
sweepBox(box5, direction, distance3, options2 = {}) {
|
|
24525
24649
|
const d = vec.normalize(direction);
|
|
24526
24650
|
const ignored = options2.ignore === void 0 ? void 0 : objects.get(options2.ignore);
|
|
24527
|
-
const
|
|
24651
|
+
const hosted = options2.ignore === void 0 || ignored ? void 0 : impls.get(options2.ignore);
|
|
24652
|
+
const ignore = ignored ? new Set(ignored.parts.map((p) => `${ignored.id}\0${p.spec.id}`)) : hosted ? new Set(hosted.impl.describe().parts.map((p) => `${hosted.impl.id}\0${p.id}`)) : void 0;
|
|
24528
24653
|
let best = null;
|
|
24529
24654
|
for (const origin of sweepOrigins(box5, d, options2.spacing ?? 0.2)) {
|
|
24530
24655
|
const r = raycastInternal(origin, d, distance3 + SWEEP_BACK, ignore);
|
|
@@ -34127,6 +34252,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34127
34252
|
function collapseRange(mesh3, v0, v1) {
|
|
34128
34253
|
const pos = mesh3.geometry.attributes.position;
|
|
34129
34254
|
const a = pos.array;
|
|
34255
|
+
const u = mesh3.userData;
|
|
34256
|
+
if (!u._p0) u._p0 = a.slice();
|
|
34257
|
+
const list2 = u.collapsedRanges ?? (u.collapsedRanges = []);
|
|
34258
|
+
if (!list2.some((r) => r[0] === v0 && r[1] === v1)) list2.push([v0, v1]);
|
|
34130
34259
|
const saved = a.slice(v0 * 3, v1 * 3);
|
|
34131
34260
|
const x = a[v0 * 3], y = a[v0 * 3 + 1], z = a[v0 * 3 + 2];
|
|
34132
34261
|
for (let k = v0; k < v1; k++) {
|
|
@@ -34141,6 +34270,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34141
34270
|
const pos = mesh3.geometry.attributes.position;
|
|
34142
34271
|
pos.array.set(saved, v0 * 3);
|
|
34143
34272
|
pos.needsUpdate = true;
|
|
34273
|
+
const list2 = mesh3.userData.collapsedRanges;
|
|
34274
|
+
if (list2) mesh3.userData.collapsedRanges = list2.filter((r) => r[0] !== v0);
|
|
34144
34275
|
}
|
|
34145
34276
|
function noise2(x, y, cell) {
|
|
34146
34277
|
const h = (i2, j2) => {
|
|
@@ -34155,13 +34286,16 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34155
34286
|
var SPALL_MIN = 0.025;
|
|
34156
34287
|
var SPALL_RAGGED = 0.045;
|
|
34157
34288
|
var COVER2 = 0.015;
|
|
34289
|
+
var MAX_FLAKES = 64;
|
|
34158
34290
|
function installDestructionBreach(K, _shared) {
|
|
34159
34291
|
const { THREE, fragmentMaterial, invalidate, invalidateShadow } = K;
|
|
34160
34292
|
function createBreachView(opts) {
|
|
34161
34293
|
const { house, structure } = opts;
|
|
34162
34294
|
const views = /* @__PURE__ */ new Map();
|
|
34295
|
+
const dirty = /* @__PURE__ */ new Set();
|
|
34296
|
+
const paintBudgetMs = opts.paintBudgetMs ?? Infinity;
|
|
34297
|
+
const dropped = [];
|
|
34163
34298
|
let lod = 0;
|
|
34164
|
-
const unitMaterial = new THREE.MeshLambertMaterial({ color: 16777215 });
|
|
34165
34299
|
const unitGeometry = new THREE.BoxGeometry(1, 1, 1);
|
|
34166
34300
|
const tmp = { m: new THREE.Matrix4(), q: new THREE.Quaternion(), p: new THREE.Vector3(), s: new THREE.Vector3(), c: new THREE.Color() };
|
|
34167
34301
|
const local2 = (p) => {
|
|
@@ -34230,6 +34364,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34230
34364
|
const mesh3 = new THREE.Mesh(g, material);
|
|
34231
34365
|
mesh3.name = "breach-shell";
|
|
34232
34366
|
mesh3.userData.breachMask = mask;
|
|
34367
|
+
mesh3.userData.cutAt = (p) => {
|
|
34368
|
+
const v = local2([p.x, p.y, p.z]);
|
|
34369
|
+
const i = Math.floor(((mask.alongU ? v[0] : v[2]) - mask.start) / mask.len * mask.w), j = Math.floor((v[1] - mask.y0) / mask.height * mask.h);
|
|
34370
|
+
return i >= 0 && i < mask.w && j >= 0 && j < mask.h && mask.data[j * mask.w + i] > 127;
|
|
34371
|
+
};
|
|
34233
34372
|
mesh3.position.copy(it.mesh.position);
|
|
34234
34373
|
mesh3.quaternion.copy(it.mesh.quaternion);
|
|
34235
34374
|
mesh3.scale.copy(it.mesh.scale);
|
|
@@ -34245,6 +34384,19 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34245
34384
|
toHouse.value.copy(house.matrixWorld).invert().multiply(mesh3.matrixWorld);
|
|
34246
34385
|
return mesh3;
|
|
34247
34386
|
}
|
|
34387
|
+
function unitsMaterial(core) {
|
|
34388
|
+
const material = new THREE.MeshLambertMaterial({ color: 16777215, side: THREE.DoubleSide });
|
|
34389
|
+
material.onBeforeCompile = (sh) => {
|
|
34390
|
+
sh.uniforms.uCore = { value: core.texture };
|
|
34391
|
+
sh.uniforms.uCoreRect = { value: core.rect };
|
|
34392
|
+
sh.uniforms.uCoreAlongX = { value: core.alongU ? 1 : 0 };
|
|
34393
|
+
sh.vertexShader = sh.vertexShader.replace("void main() {", "varying vec3 vHousePos;\nvoid main() {").replace("#include <begin_vertex>", "#include <begin_vertex>\n#ifdef USE_INSTANCING\nvHousePos = (instanceMatrix * vec4(transformed, 1.0)).xyz;\n#else\nvHousePos = transformed;\n#endif");
|
|
34394
|
+
sh.fragmentShader = sh.fragmentShader.replace("void main() {", "uniform sampler2D uCore;\nuniform vec4 uCoreRect;\nuniform float uCoreAlongX;\nvarying vec3 vHousePos;\nvoid main() {\n vec2 cUv = vec2(((uCoreAlongX > 0.5 ? vHousePos.x : vHousePos.z) - uCoreRect.x) / uCoreRect.z, (vHousePos.y - uCoreRect.y) / uCoreRect.w);\n if (cUv.x >= 0.0 && cUv.x <= 1.0 && cUv.y >= 0.0 && cUv.y <= 1.0 && texture2D(uCore, cUv).r > 0.5) discard;").replace("#include <color_fragment>", "#include <color_fragment>\nif (!gl_FrontFacing) diffuseColor.rgb *= 0.55;");
|
|
34395
|
+
};
|
|
34396
|
+
material.customProgramCacheKey = () => "breachCore";
|
|
34397
|
+
material.userData.breachCore = core;
|
|
34398
|
+
return material;
|
|
34399
|
+
}
|
|
34248
34400
|
function maskOf(hostId, parts) {
|
|
34249
34401
|
const host = structure.byId.get(hostId);
|
|
34250
34402
|
const [u0, v0, u1, v1] = host.aabb;
|
|
@@ -34260,41 +34412,73 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34260
34412
|
return { data, texture, w, h, alongU, start: alongU ? u0 : v0, len, y0, height: y1 - y0, rect: new THREE.Vector4(alongU ? u0 : v0, y0, len, y1 - y0) };
|
|
34261
34413
|
}
|
|
34262
34414
|
const boxDistance = (s, y, a, b, c, d) => Math.hypot(Math.max(a - s, 0, s - b), Math.max(c - y, 0, y - d));
|
|
34415
|
+
const ellipseDistance = (s, y, cs, cy, rs, ry) => (Math.hypot((s - cs) / Math.max(rs, 1e-4), (y - cy) / Math.max(ry, 1e-4)) - 1) * Math.min(rs, ry);
|
|
34416
|
+
function covered(c, boxes) {
|
|
34417
|
+
const n = 8;
|
|
34418
|
+
let hit = 0;
|
|
34419
|
+
for (let j = 0; j < n; j++) {
|
|
34420
|
+
const y = c.y0 + (j + 0.5) / n * (c.y1 - c.y0);
|
|
34421
|
+
for (let i = 0; i < n; i++) {
|
|
34422
|
+
const s = c.a + (i + 0.5) / n * (c.b - c.a);
|
|
34423
|
+
if (boxes.some((k) => s >= k.s0 && s <= k.s1 && y >= k.y0 && y <= k.y1)) hit++;
|
|
34424
|
+
}
|
|
34425
|
+
}
|
|
34426
|
+
return hit / (n * n);
|
|
34427
|
+
}
|
|
34263
34428
|
function paint2(view) {
|
|
34264
|
-
const { mask } = view;
|
|
34429
|
+
const { mask, core } = view;
|
|
34265
34430
|
const along = (p) => (mask.alongU ? p[0] : p[2]) - mask.start;
|
|
34266
34431
|
const holes = [];
|
|
34432
|
+
const pits = [];
|
|
34267
34433
|
const discs = [];
|
|
34434
|
+
const flakes = [];
|
|
34268
34435
|
const shown = [];
|
|
34269
34436
|
for (const id of view.parts) {
|
|
34270
|
-
const part = structure.byId.get(id), units2 = specOf(id).units, chips2 = view.chips.get(id), opening = view.openings.get(id);
|
|
34437
|
+
const part = structure.byId.get(id), units2 = specOf(id).units, chips2 = view.chips.get(id), opening = view.openings.get(id), spalls = view.spalls.get(id);
|
|
34271
34438
|
const all = cells(part, units2);
|
|
34272
34439
|
const thick = mask.alongU ? part.aabb[3] - part.aabb[1] : part.aabb[2] - part.aabb[0];
|
|
34273
34440
|
const mid = mask.alongU ? (part.aabb[1] + part.aabb[3]) / 2 : (part.aabb[0] + part.aabb[2]) / 2;
|
|
34274
34441
|
if (opening) discs.push({ s: along(opening.center), y: opening.center[1], r: opening.radius });
|
|
34275
|
-
|
|
34442
|
+
for (const k of spalls) flakes.push({ s: along(k.center), y: k.center[1], r: k.radius });
|
|
34443
|
+
const boxes = chips2.map((k) => ({ s0: along(k.center) - k.along, s1: along(k.center) + k.along, y0: k.center[1] - k.up, y1: k.center[1] + k.up }));
|
|
34444
|
+
const gone = (c) => covered(c, boxes.filter((k) => k.s1 > c.a && k.s0 < c.b && k.y1 > c.y0 && k.y0 < c.y1)) >= 0.9 || opening && Math.hypot((c.a + c.b) / 2 - along(opening.center), (c.y0 + c.y1) / 2 - opening.center[1]) < opening.radius;
|
|
34276
34445
|
const kept = [];
|
|
34277
34446
|
for (const c of all) (gone(c) ? holes : kept).push(c);
|
|
34278
34447
|
for (const c of kept) shown.push({ c, part, units: units2, thick, mid });
|
|
34448
|
+
for (const k of boxes) {
|
|
34449
|
+
if (holes.some((h) => k.s0 >= h.a - 1e-4 && k.s1 <= h.b + 1e-4 && k.y0 >= h.y0 - 1e-4 && k.y1 <= h.y1 + 1e-4)) continue;
|
|
34450
|
+
pits.push({ s: (k.s0 + k.s1) / 2, y: (k.y0 + k.y1) / 2, rs: (k.s1 - k.s0) / 2, ry: (k.y1 - k.y0) / 2 });
|
|
34451
|
+
}
|
|
34279
34452
|
}
|
|
34280
34453
|
const reach = SPALL_MIN + SPALL_RAGGED;
|
|
34454
|
+
const ragged = (s, y) => 0.6 * noise2(s, y, 0.03) + 0.4 * noise2(s + 7.3, y - 3.1, 0.011);
|
|
34281
34455
|
mask.data.fill(0);
|
|
34282
|
-
|
|
34283
|
-
|
|
34284
|
-
const
|
|
34456
|
+
core.data.fill(0);
|
|
34457
|
+
const stamp = (m, s0, s1, y0, y1, inside2) => {
|
|
34458
|
+
const i0 = Math.max(0, Math.floor((s0 - reach) / m.len * m.w)), i1 = Math.min(m.w - 1, Math.ceil((s1 + reach) / m.len * m.w));
|
|
34459
|
+
const j0 = Math.max(0, Math.floor((y0 - reach - m.y0) / m.height * m.h)), j1 = Math.min(m.h - 1, Math.ceil((y1 + reach - m.y0) / m.height * m.h));
|
|
34285
34460
|
for (let j = j0; j <= j1; j++) {
|
|
34286
|
-
const y =
|
|
34461
|
+
const y = m.y0 + (j + 0.5) / m.h * m.height;
|
|
34287
34462
|
for (let i = i0; i <= i1; i++) {
|
|
34288
|
-
const s = (i + 0.5) /
|
|
34289
|
-
|
|
34290
|
-
if (inside2(s, y) <= edge) mask.data[j * mask.w + i] = 255;
|
|
34463
|
+
const s = (i + 0.5) / m.w * m.len;
|
|
34464
|
+
if (inside2(s, y)) m.data[j * m.w + i] = 255;
|
|
34291
34465
|
}
|
|
34292
34466
|
}
|
|
34293
34467
|
};
|
|
34294
|
-
|
|
34295
|
-
for (const
|
|
34468
|
+
const plaster = (d, s, y) => d <= SPALL_MIN + SPALL_RAGGED * ragged(s, y);
|
|
34469
|
+
for (const c of holes) stamp(mask, c.a, c.b, c.y0, c.y1, (s, y) => plaster(boxDistance(s, y, c.a, c.b, c.y0, c.y1), s, y));
|
|
34470
|
+
for (const d of discs) stamp(mask, d.s - d.r, d.s + d.r, d.y - d.r, d.y + d.r, (s, y) => plaster(Math.max(0, Math.hypot(s - d.s, y - d.y) - d.r), s, y));
|
|
34471
|
+
for (const f of flakes) {
|
|
34472
|
+
const rough = (s, y) => Math.hypot(s - f.s, y - f.y) - f.r * (0.65 + 0.7 * ragged(s * 2.3, y * 2.3));
|
|
34473
|
+
stamp(mask, f.s - 2 * f.r, f.s + 2 * f.r, f.y - 2 * f.r, f.y + 2 * f.r, (s, y) => rough(s, y) <= 0);
|
|
34474
|
+
}
|
|
34475
|
+
for (const p of pits) {
|
|
34476
|
+
stamp(mask, p.s - p.rs, p.s + p.rs, p.y - p.ry, p.y + p.ry, (s, y) => plaster(Math.max(0, ellipseDistance(s, y, p.s, p.y, p.rs, p.ry)), s, y));
|
|
34477
|
+
stamp(core, p.s - p.rs, p.s + p.rs, p.y - p.ry, p.y + p.ry, (s, y) => ellipseDistance(s, y, p.s, p.y, p.rs, p.ry) <= -0.35 * Math.min(p.rs, p.ry) * noise2(s * 1.7, y * 1.3, 0.012));
|
|
34478
|
+
}
|
|
34296
34479
|
mask.texture.needsUpdate = true;
|
|
34297
|
-
|
|
34480
|
+
core.texture.needsUpdate = true;
|
|
34481
|
+
const near = (c) => holes.some((h) => boxDistance((c.a + c.b) / 2, (c.y0 + c.y1) / 2, h.a - (c.b - c.a) / 2, h.b + (c.b - c.a) / 2, h.y0 - (c.y1 - c.y0) / 2, h.y1 + (c.y1 - c.y0) / 2) <= reach) || discs.some((d) => boxDistance(d.s, d.y, c.a, c.b, c.y0, c.y1) <= d.r + reach) || pits.some((p) => boxDistance(p.s, p.y, c.a, c.b, c.y0, c.y1) <= Math.max(p.rs, p.ry) + reach) || flakes.some((f) => boxDistance(f.s, f.y, c.a, c.b, c.y0, c.y1) <= 2 * f.r);
|
|
34298
34482
|
const list2 = [];
|
|
34299
34483
|
const tone = (hex2, k) => tmp.c.setHex(hex2).multiplyScalar(k).getHex();
|
|
34300
34484
|
for (const { c, part, units: units2, thick, mid } of shown) {
|
|
@@ -34304,7 +34488,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34304
34488
|
const base = fragmentMaterial(part.material).color.getHex();
|
|
34305
34489
|
const n = noise2(centre * 3.1, y * 2.7, 0.07);
|
|
34306
34490
|
if (units2.mortar === "concrete") {
|
|
34307
|
-
at3(len, h, thick - 2 * COVER2
|
|
34491
|
+
at3(len, h, thick - 2 * COVER2, tone(base, 0.9 + 0.04 * n));
|
|
34308
34492
|
} else {
|
|
34309
34493
|
at3(len, h, thick - 2 * COVER2 - 0.012, 10327948);
|
|
34310
34494
|
at3(len - 0.012, h - 0.012, thick - 2 * COVER2, tone(base, 0.8 + 0.35 * n));
|
|
@@ -34314,7 +34498,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34314
34498
|
view.units.removeFromParent();
|
|
34315
34499
|
view.units.dispose();
|
|
34316
34500
|
}
|
|
34317
|
-
const units = new THREE.InstancedMesh(unitGeometry, unitMaterial, Math.max(1, list2.length));
|
|
34501
|
+
const units = new THREE.InstancedMesh(unitGeometry, view.unitMaterial, Math.max(1, list2.length));
|
|
34318
34502
|
units.name = "breach:" + view.host;
|
|
34319
34503
|
list2.forEach((u, i) => {
|
|
34320
34504
|
tmp.m.compose(tmp.p.set(u.x, u.y, u.z), tmp.q.identity(), tmp.s.set(u.sx, u.sy, u.sz));
|
|
@@ -34330,6 +34514,19 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34330
34514
|
house.add(units);
|
|
34331
34515
|
view.units = units;
|
|
34332
34516
|
view.gone = holes.length;
|
|
34517
|
+
view.pits = pits.length;
|
|
34518
|
+
view.flakes = flakes.length;
|
|
34519
|
+
}
|
|
34520
|
+
function chipBox(chip, alongU) {
|
|
34521
|
+
house.updateMatrixWorld(true);
|
|
34522
|
+
const q = tmp.q.set(chip.rotation[0], chip.rotation[1], chip.rotation[2], chip.rotation[3]).premultiply(house.getWorldQuaternion(new THREE.Quaternion()).invert());
|
|
34523
|
+
let a = 0, u = 0;
|
|
34524
|
+
for (let k = 0; k < 3; k++) {
|
|
34525
|
+
const axis2 = tmp.p.set(k === 0 ? 1 : 0, k === 1 ? 1 : 0, k === 2 ? 1 : 0).applyQuaternion(q);
|
|
34526
|
+
a += Math.abs(alongU ? axis2.x : axis2.z) * chip.size[k] / 2;
|
|
34527
|
+
u += Math.abs(axis2.y) * chip.size[k] / 2;
|
|
34528
|
+
}
|
|
34529
|
+
return { center: local2(chip.center), along: a, up: u };
|
|
34333
34530
|
}
|
|
34334
34531
|
function draw2(partId, change) {
|
|
34335
34532
|
if (lod !== 0) return false;
|
|
@@ -34341,23 +34538,49 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34341
34538
|
if (!items.length) return false;
|
|
34342
34539
|
const parts = [hostId, ...riders(hostId)].filter((id) => structure.byId.get(id) && specOf(id)?.units);
|
|
34343
34540
|
if (!parts.includes(partId)) return false;
|
|
34344
|
-
const mask = maskOf(hostId, parts);
|
|
34345
|
-
view = {
|
|
34541
|
+
const mask = maskOf(hostId, parts), core = maskOf(hostId, parts);
|
|
34542
|
+
view = {
|
|
34543
|
+
host: hostId,
|
|
34544
|
+
parts,
|
|
34545
|
+
items,
|
|
34546
|
+
mask,
|
|
34547
|
+
core,
|
|
34548
|
+
unitMaterial: unitsMaterial(core),
|
|
34549
|
+
shells: [],
|
|
34550
|
+
saved: null,
|
|
34551
|
+
units: null,
|
|
34552
|
+
gone: 0,
|
|
34553
|
+
pits: 0,
|
|
34554
|
+
chips: new Map(parts.map((id) => [id, []])),
|
|
34555
|
+
spalls: new Map(parts.map((id) => [id, []])),
|
|
34556
|
+
openings: /* @__PURE__ */ new Map(),
|
|
34557
|
+
flakes: 0
|
|
34558
|
+
};
|
|
34346
34559
|
}
|
|
34347
34560
|
if (!view.chips.has(partId)) return false;
|
|
34348
34561
|
if (change.opening) {
|
|
34349
34562
|
if (view.openings.has(partId)) return false;
|
|
34350
34563
|
view.openings.set(partId, { center: local2(change.opening.center), radius: change.opening.radius });
|
|
34351
34564
|
}
|
|
34352
|
-
if (change.chip) view.chips.get(partId).push(
|
|
34565
|
+
if (change.chip) view.chips.get(partId).push(chipBox(change.chip, view.mask.alongU));
|
|
34566
|
+
if (change.spall) {
|
|
34567
|
+
const list2 = view.spalls.get(partId);
|
|
34568
|
+
list2.push({ center: local2(change.spall.center), radius: change.spall.radius });
|
|
34569
|
+
if (list2.length > MAX_FLAKES) list2.shift();
|
|
34570
|
+
}
|
|
34353
34571
|
if (!view.saved) {
|
|
34354
34572
|
view.shells = view.items.map((it) => shell(it, view.mask));
|
|
34573
|
+
for (const m of view.shells) m.userData.behind = () => view.units;
|
|
34355
34574
|
view.saved = view.items.map((it) => ({ it, v0: it.v0, data: collapseRange(it.mesh, it.v0, it.v1) }));
|
|
34356
34575
|
}
|
|
34357
|
-
paint2(view);
|
|
34358
34576
|
views.set(hostId, view);
|
|
34359
|
-
|
|
34360
|
-
|
|
34577
|
+
if (opts.deferPaint && change.spall) dirty.add(view);
|
|
34578
|
+
else {
|
|
34579
|
+
dirty.delete(view);
|
|
34580
|
+
paint2(view);
|
|
34581
|
+
invalidate();
|
|
34582
|
+
invalidateShadow();
|
|
34583
|
+
}
|
|
34361
34584
|
return true;
|
|
34362
34585
|
}
|
|
34363
34586
|
function plain2(view, shown) {
|
|
@@ -34366,24 +34589,70 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34366
34589
|
else s.data = collapseRange(s.it.mesh, s.v0, s.it.v1);
|
|
34367
34590
|
}
|
|
34368
34591
|
}
|
|
34592
|
+
function flush(budgetMs = paintBudgetMs) {
|
|
34593
|
+
if (dirty.size === 0) return 0;
|
|
34594
|
+
const deadline = performance.now() + budgetMs;
|
|
34595
|
+
for (const view of dirty) {
|
|
34596
|
+
dirty.delete(view);
|
|
34597
|
+
paint2(view);
|
|
34598
|
+
if (performance.now() >= deadline) break;
|
|
34599
|
+
}
|
|
34600
|
+
invalidate();
|
|
34601
|
+
invalidateShadow();
|
|
34602
|
+
return dirty.size;
|
|
34603
|
+
}
|
|
34369
34604
|
function dispose(view) {
|
|
34370
|
-
|
|
34371
|
-
view.units
|
|
34605
|
+
dirty.delete(view);
|
|
34606
|
+
if (view.units) {
|
|
34607
|
+
view.units.removeFromParent();
|
|
34608
|
+
view.units.dispose();
|
|
34609
|
+
}
|
|
34610
|
+
view.unitMaterial.dispose();
|
|
34372
34611
|
for (const m of view.shells) {
|
|
34373
34612
|
m.removeFromParent();
|
|
34374
34613
|
m.geometry.dispose();
|
|
34375
34614
|
m.material.dispose();
|
|
34376
34615
|
}
|
|
34377
34616
|
view.mask.texture.dispose();
|
|
34617
|
+
view.core.texture.dispose();
|
|
34378
34618
|
}
|
|
34379
34619
|
return {
|
|
34380
34620
|
/** Handles a `stage` event of the house's object. Returns true when the wall was cut. */
|
|
34381
34621
|
stage(event) {
|
|
34382
34622
|
return event.stage === 2 && !!event.opening ? draw2(event.part, { opening: event.opening }) : false;
|
|
34383
34623
|
},
|
|
34384
|
-
/**
|
|
34624
|
+
/**
|
|
34625
|
+
* Handles a `spall` of a wall of units: the coat comes off in a ragged patch and the units show under it, drawn by the house (the destruction
|
|
34626
|
+
* renderer leaves its flat patch out on the meshes `claim` marked). Returns true when the wall was cut.
|
|
34627
|
+
*/
|
|
34628
|
+
spall(event) {
|
|
34629
|
+
const r = Math.sqrt(Math.max(0, event.area) / Math.PI);
|
|
34630
|
+
return r > 0 ? draw2(event.part, { spall: { center: event.point, radius: r } }) : false;
|
|
34631
|
+
},
|
|
34632
|
+
/**
|
|
34633
|
+
* Marks the meshes that draw a wall of units as drawing its spalls itself (`userData.drawnSpalls`, the part ids): the destruction renderer then
|
|
34634
|
+
* throws the bits and leaves its patch out. Call it once the spec is known.
|
|
34635
|
+
*/
|
|
34636
|
+
claim() {
|
|
34637
|
+
var _a;
|
|
34638
|
+
for (const q of opts.spec()?.parts ?? []) {
|
|
34639
|
+
if (!q.units) continue;
|
|
34640
|
+
const host = hostOf(q.id);
|
|
34641
|
+
if (!host) continue;
|
|
34642
|
+
for (const it of structure.meshesOf(host)) {
|
|
34643
|
+
if (!it.mesh) continue;
|
|
34644
|
+
const list2 = (_a = it.mesh.userData).drawnSpalls ?? (_a.drawnSpalls = []);
|
|
34645
|
+
if (!list2.includes(q.id)) list2.push(q.id);
|
|
34646
|
+
}
|
|
34647
|
+
}
|
|
34648
|
+
},
|
|
34649
|
+
/**
|
|
34650
|
+
* Handles a `chip` event: what came out (a unit, or a crater a shot broke through one) leaves a hole in the wall where it was; a `torn` chip (what a blow
|
|
34651
|
+
* tore off a face) leaves the marks the renderer drew. Returns true when the wall was cut.
|
|
34652
|
+
*/
|
|
34385
34653
|
chip(event) {
|
|
34386
|
-
|
|
34654
|
+
if (event.torn) return false;
|
|
34655
|
+
return draw2(event.part, { chip: { center: event.center, size: event.size ?? [0, 0, 0], rotation: event.rotation ?? [0, 0, 0, 1] } });
|
|
34387
34656
|
},
|
|
34388
34657
|
/** The part broke or fell: its damage goes (the plain range stays collapsed, the renderer hides the part). */
|
|
34389
34658
|
drop(partId) {
|
|
@@ -34391,9 +34660,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34391
34660
|
if (!v) return;
|
|
34392
34661
|
dispose(v);
|
|
34393
34662
|
views.delete(partId);
|
|
34663
|
+
if (v.saved) dropped.push(v);
|
|
34394
34664
|
},
|
|
34395
34665
|
/** LOD 0 draws the cut wall; LOD 1 and 2 give the wall its box back. */
|
|
34396
34666
|
setLod(level) {
|
|
34667
|
+
flush(Infinity);
|
|
34397
34668
|
const was = lod;
|
|
34398
34669
|
lod = level;
|
|
34399
34670
|
if (was === 0 === (level === 0)) return;
|
|
@@ -34412,21 +34683,27 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34412
34683
|
if (lod === 0) plain2(v, true);
|
|
34413
34684
|
}
|
|
34414
34685
|
views.clear();
|
|
34686
|
+
for (const v of dropped) plain2(v, true);
|
|
34687
|
+
dropped.length = 0;
|
|
34415
34688
|
},
|
|
34689
|
+
flush,
|
|
34416
34690
|
/** The wall has damage drawn. */
|
|
34417
34691
|
has(partId) {
|
|
34418
34692
|
return views.has(partId);
|
|
34419
34693
|
},
|
|
34420
|
-
/** Walls with damage drawn, units out of them, and units laid bare at the edge of the cuts. */
|
|
34421
|
-
statistics: () => ({
|
|
34694
|
+
/** Walls with damage drawn, units out of them, craters shots broke through units that stay, and units laid bare at the edge of the cuts. */
|
|
34695
|
+
statistics: () => (flush(Infinity), {
|
|
34422
34696
|
walls: views.size,
|
|
34423
34697
|
gone: [...views.values()].reduce((n, v) => n + v.gone, 0),
|
|
34698
|
+
craters: [...views.values()].reduce((n, v) => n + v.pits, 0),
|
|
34699
|
+
flakes: [...views.values()].reduce((n, v) => n + v.flakes, 0),
|
|
34424
34700
|
units: [...views.values()].reduce((n, v) => n + v.units.count, 0)
|
|
34425
34701
|
})
|
|
34426
34702
|
};
|
|
34427
34703
|
}
|
|
34428
34704
|
Object.assign(K, { createBreachView });
|
|
34429
34705
|
}
|
|
34706
|
+
var FOLLOW_FLUSH_FALLBACK_MS = 100;
|
|
34430
34707
|
function installDestructionWorld(K, shared) {
|
|
34431
34708
|
const { THREE, cfg, invalidateShadow, invalidate, rngString, FRAGMENT_GEO, fragmentMaterial, createBreachView } = K;
|
|
34432
34709
|
function createHouseDestruction(root, house, structure, parts, colliders, water) {
|
|
@@ -34435,7 +34712,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34435
34712
|
const R = cfg.destructionRenderer || null, listeners = /* @__PURE__ */ new Set();
|
|
34436
34713
|
D.renderer = R;
|
|
34437
34714
|
D.spec = null;
|
|
34438
|
-
const breach = createBreachView({ house, structure, spec: () => D.spec });
|
|
34715
|
+
const breach = createBreachView({ house, structure, spec: () => D.spec, deferPaint: true, ...cfg.breachPaintBudgetMs !== void 0 ? { paintBudgetMs: cfg.breachPaintBudgetMs } : {} });
|
|
34439
34716
|
D.breach = breach;
|
|
34440
34717
|
D.onEvent = (fn) => {
|
|
34441
34718
|
listeners.add(fn);
|
|
@@ -34620,6 +34897,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34620
34897
|
case "chip":
|
|
34621
34898
|
breach.chip(e);
|
|
34622
34899
|
break;
|
|
34900
|
+
case "spall":
|
|
34901
|
+
breach.spall(e);
|
|
34902
|
+
break;
|
|
34623
34903
|
case "collapse":
|
|
34624
34904
|
for (const id of e.parts) {
|
|
34625
34905
|
breach.drop(id);
|
|
@@ -34661,7 +34941,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34661
34941
|
D.specOptions = specOptions || null;
|
|
34662
34942
|
D.spec = structure.spec(specOptions);
|
|
34663
34943
|
D.id = D.world.add(D.spec, { id: "house3d:" + ++shared.destructionSeq, position: [p.x, p.y, p.z], rotation: [q.x, q.y, q.z, q.w] });
|
|
34664
|
-
|
|
34944
|
+
breach.claim();
|
|
34945
|
+
if (R) R.bind(D.id, house, { parts: rangesOf(), onFrame: () => breach.flush() });
|
|
34665
34946
|
off = D.world.on("*", handleEvent);
|
|
34666
34947
|
D.active = true;
|
|
34667
34948
|
return D;
|
|
@@ -34676,6 +34957,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34676
34957
|
};
|
|
34677
34958
|
D.follow = (world, id, spec) => {
|
|
34678
34959
|
D.spec = spec;
|
|
34960
|
+
breach.claim();
|
|
34961
|
+
let fallback2 = null;
|
|
34679
34962
|
const offFollow = world.on("*", (e) => {
|
|
34680
34963
|
if (e.object !== id) return;
|
|
34681
34964
|
switch (e.type) {
|
|
@@ -34685,6 +34968,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34685
34968
|
case "chip":
|
|
34686
34969
|
breach.chip(e);
|
|
34687
34970
|
break;
|
|
34971
|
+
case "spall":
|
|
34972
|
+
breach.spall(e);
|
|
34973
|
+
break;
|
|
34688
34974
|
case "fracture":
|
|
34689
34975
|
breach.drop(e.part);
|
|
34690
34976
|
break;
|
|
@@ -34694,10 +34980,16 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34694
34980
|
default:
|
|
34695
34981
|
break;
|
|
34696
34982
|
}
|
|
34983
|
+
if (!fallback2 && typeof setTimeout === "function") fallback2 = setTimeout(() => {
|
|
34984
|
+
fallback2 = null;
|
|
34985
|
+
breach.flush(Infinity);
|
|
34986
|
+
}, FOLLOW_FLUSH_FALLBACK_MS);
|
|
34697
34987
|
});
|
|
34698
34988
|
return { restore: () => breach.restore(), dispose: () => {
|
|
34699
34989
|
offFollow();
|
|
34700
34990
|
breach.restore();
|
|
34991
|
+
}, update: () => {
|
|
34992
|
+
breach.flush();
|
|
34701
34993
|
} };
|
|
34702
34994
|
};
|
|
34703
34995
|
D.piece = (id) => D.active ? D.world.part(D.id, id) : null;
|
|
@@ -34730,6 +35022,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34730
35022
|
return r;
|
|
34731
35023
|
};
|
|
34732
35024
|
D.animate = (dt) => {
|
|
35025
|
+
breach.flush();
|
|
34733
35026
|
if (R) return;
|
|
34734
35027
|
let moved = false;
|
|
34735
35028
|
for (const [id, list2] of bodies) {
|
|
@@ -36752,6 +37045,54 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
36752
37045
|
store.buffer ?? (store.buffer = bufferOf(context, [samples], context.sampleRate * seconds));
|
|
36753
37046
|
return store.buffer;
|
|
36754
37047
|
}
|
|
37048
|
+
var nowMs = () => typeof performance === "undefined" ? Date.now() : performance.now();
|
|
37049
|
+
function scheduleTask(task) {
|
|
37050
|
+
if (typeof MessageChannel === "undefined") {
|
|
37051
|
+
setTimeout(task, 0);
|
|
37052
|
+
return;
|
|
37053
|
+
}
|
|
37054
|
+
const channel = new MessageChannel();
|
|
37055
|
+
channel.port1.onmessage = () => {
|
|
37056
|
+
channel.port1.close();
|
|
37057
|
+
task();
|
|
37058
|
+
};
|
|
37059
|
+
channel.port2.postMessage(0);
|
|
37060
|
+
}
|
|
37061
|
+
function createStartQueue(options = {}) {
|
|
37062
|
+
const budgetMs = options.budgetMs ?? 2;
|
|
37063
|
+
const maxLatencyMs = options.maxLatencyMs ?? 250;
|
|
37064
|
+
let jobs = [];
|
|
37065
|
+
let scheduled = false;
|
|
37066
|
+
const drain = () => {
|
|
37067
|
+
scheduled = false;
|
|
37068
|
+
const began = nowMs();
|
|
37069
|
+
while (jobs.length > 0) {
|
|
37070
|
+
const job = jobs.shift();
|
|
37071
|
+
if (nowMs() - job.queuedAt > maxLatencyMs) job.drop();
|
|
37072
|
+
else job.run();
|
|
37073
|
+
if (nowMs() - began >= budgetMs) break;
|
|
37074
|
+
}
|
|
37075
|
+
if (jobs.length > 0) schedule();
|
|
37076
|
+
};
|
|
37077
|
+
const schedule = () => {
|
|
37078
|
+
if (scheduled) return;
|
|
37079
|
+
scheduled = true;
|
|
37080
|
+
scheduleTask(drain);
|
|
37081
|
+
};
|
|
37082
|
+
return {
|
|
37083
|
+
push(job) {
|
|
37084
|
+
jobs.push({ ...job, queuedAt: nowMs() });
|
|
37085
|
+
schedule();
|
|
37086
|
+
},
|
|
37087
|
+
clear() {
|
|
37088
|
+
for (const job of jobs) job.drop();
|
|
37089
|
+
jobs = [];
|
|
37090
|
+
},
|
|
37091
|
+
get size() {
|
|
37092
|
+
return jobs.length;
|
|
37093
|
+
}
|
|
37094
|
+
};
|
|
37095
|
+
}
|
|
36755
37096
|
var end = (layer2, start) => start + layer2.duration + layer2.tail;
|
|
36756
37097
|
var firstFilter = (layer2) => layer2.filters?.bank[0];
|
|
36757
37098
|
var offsetOf = (layer2) => layer2.source.kind === "noise" ? layer2.source.offset : 0;
|
|
@@ -37192,15 +37533,43 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
37192
37533
|
...playOptions.params ? { params: playOptions.params } : {},
|
|
37193
37534
|
...options.occlusion && playOptions.position && recipe.falloff?.model !== "none" ? { occlusion: options.occlusion(playOptions.position) } : {}
|
|
37194
37535
|
});
|
|
37195
|
-
const
|
|
37536
|
+
const startQueue = options.deferredStart ? createStartQueue(options.deferredStart) : null;
|
|
37537
|
+
const build = (plan, store, delay) => {
|
|
37196
37538
|
const audio = context;
|
|
37197
|
-
|
|
37539
|
+
return realize(plan, {
|
|
37198
37540
|
context: audio,
|
|
37199
37541
|
output: outputFor(plan.bus),
|
|
37200
37542
|
...chain?.wet ? { wet: chain.wet } : {},
|
|
37201
37543
|
noise: () => noiseBuffer(store, audio, mix3.noise.seconds),
|
|
37202
37544
|
time: audio.currentTime + delay
|
|
37203
37545
|
});
|
|
37546
|
+
};
|
|
37547
|
+
const deferVoice = (plan, store, voiceId, delay) => {
|
|
37548
|
+
let built = null;
|
|
37549
|
+
let cancelled = false;
|
|
37550
|
+
const pending = {
|
|
37551
|
+
get sources() {
|
|
37552
|
+
return built?.sources ?? [];
|
|
37553
|
+
},
|
|
37554
|
+
stop() {
|
|
37555
|
+
cancelled = true;
|
|
37556
|
+
built?.stop();
|
|
37557
|
+
}
|
|
37558
|
+
};
|
|
37559
|
+
startQueue.push({
|
|
37560
|
+
run() {
|
|
37561
|
+
if (!cancelled && context) built = build(plan, store, delay);
|
|
37562
|
+
},
|
|
37563
|
+
drop() {
|
|
37564
|
+
if (voiceId === void 0) return;
|
|
37565
|
+
voices.release(voiceId);
|
|
37566
|
+
active.delete(voiceId);
|
|
37567
|
+
}
|
|
37568
|
+
});
|
|
37569
|
+
return pending;
|
|
37570
|
+
};
|
|
37571
|
+
const start = (plan, store, voiceId, delay = 0) => {
|
|
37572
|
+
const realized = startQueue ? deferVoice(plan, store, voiceId, delay) : build(plan, store, delay);
|
|
37204
37573
|
if (voiceId === void 0) return null;
|
|
37205
37574
|
voices.setLength(voiceId, plan.length + delay);
|
|
37206
37575
|
active.set(voiceId, realized);
|
|
@@ -37316,6 +37685,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
37316
37685
|
const audio = ready();
|
|
37317
37686
|
if (audio) unlockContext(audio);
|
|
37318
37687
|
},
|
|
37688
|
+
warmUp() {
|
|
37689
|
+
const audio = ready();
|
|
37690
|
+
if (!audio) return false;
|
|
37691
|
+
noiseBuffer(shared, audio, mix3.noise.seconds);
|
|
37692
|
+
return true;
|
|
37693
|
+
},
|
|
37319
37694
|
level(bus) {
|
|
37320
37695
|
watched.add(bus);
|
|
37321
37696
|
ensureBus(bus);
|
|
@@ -37339,6 +37714,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
37339
37714
|
};
|
|
37340
37715
|
},
|
|
37341
37716
|
dispose() {
|
|
37717
|
+
startQueue?.clear();
|
|
37342
37718
|
for (const id of active.keys()) voices.release(id);
|
|
37343
37719
|
for (const loop of [...loops]) loop.stop();
|
|
37344
37720
|
active.clear();
|
|
@@ -38574,6 +38950,83 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
38574
38950
|
};
|
|
38575
38951
|
};
|
|
38576
38952
|
|
|
38953
|
+
// src/fx/body-hold.ts
|
|
38954
|
+
var BODY_HOLD = {
|
|
38955
|
+
/** With no body to lend the frame: the pocket of the right shoulder from the eye, in the eye's yaw frame (right, up, back). */
|
|
38956
|
+
eyeToPocket: [0.2, -0.275, 0.17],
|
|
38957
|
+
/** A shouldered gun's bore runs this far above the shoulder pocket (the cheek on the comb). */
|
|
38958
|
+
boreAbove: 0.05,
|
|
38959
|
+
/** A handgun's (or a short two-hand gun's) grip: toward the middle of the chest, a little low, at the reach of bent arms. */
|
|
38960
|
+
handgunGrip: [-0.15, -0.04, -0.42],
|
|
38961
|
+
/** A one-hand tool's grip: low in front of the body, the elbow bent. */
|
|
38962
|
+
toolGrip: [0.02, -0.34, -0.3],
|
|
38963
|
+
/** A tool at least this long, held by two hands, is shouldered (its stock in the pocket). */
|
|
38964
|
+
shoulderedFrom: 0.55,
|
|
38965
|
+
/** The farthest a shouldered gun's grip sits ahead of the pocket; a launcher whose grip is farther slides back, its tube behind the shoulder. */
|
|
38966
|
+
shoulderGripMax: 0.42,
|
|
38967
|
+
/** The nearest a shouldered gun's grip sits ahead of the pocket; a gun with no stock (a pistol grip at its rear) slides forward, its grip not behind the eye. */
|
|
38968
|
+
shoulderGripMin: 0.25,
|
|
38969
|
+
/** Where such a grip is brought to, ahead of the pocket. */
|
|
38970
|
+
shoulderGripTo: 0.34,
|
|
38971
|
+
/** The farthest apart two hands hold a long gun (a support hand past it slides back along the gun, toward the grip). */
|
|
38972
|
+
supportSpan: 0.5,
|
|
38973
|
+
/** How much of the aim's pitch a tool held low in one hand follows. */
|
|
38974
|
+
toolPitch: 0.5
|
|
38975
|
+
};
|
|
38976
|
+
var scratch = { a: null, b: null, euler: null };
|
|
38977
|
+
var placeOnBody = (placement) => {
|
|
38978
|
+
const { THREE, hand, holder, frame: frame2 } = placement;
|
|
38979
|
+
scratch.a ?? (scratch.a = new THREE.Vector3());
|
|
38980
|
+
scratch.b ?? (scratch.b = new THREE.Vector3());
|
|
38981
|
+
holder.scale.setScalar(1);
|
|
38982
|
+
hand.position.set(...frame2.position);
|
|
38983
|
+
hand.quaternion.set(...frame2.quaternion);
|
|
38984
|
+
const shouldered = !placement.handgun && placement.left !== null && placement.lengthMetres >= BODY_HOLD.shoulderedFrom;
|
|
38985
|
+
if (shouldered) {
|
|
38986
|
+
holder.position.set(0, BODY_HOLD.boreAbove, placement.rear);
|
|
38987
|
+
hand.updateMatrixWorld(true);
|
|
38988
|
+
if (!placement.right) return;
|
|
38989
|
+
const grip2 = hand.worldToLocal(placement.right.part.localToWorld(scratch.a.copy(placement.right.offset)));
|
|
38990
|
+
if (-grip2.z <= BODY_HOLD.shoulderGripMax && -grip2.z >= BODY_HOLD.shoulderGripMin) return;
|
|
38991
|
+
holder.position.z += -grip2.z - BODY_HOLD.shoulderGripTo;
|
|
38992
|
+
hand.updateMatrixWorld(true);
|
|
38993
|
+
return;
|
|
38994
|
+
}
|
|
38995
|
+
scratch.euler ?? (scratch.euler = new THREE.Euler());
|
|
38996
|
+
const turn2 = scratch.euler.setFromQuaternion(hand.quaternion, "YXZ");
|
|
38997
|
+
hand.quaternion.setFromEuler(turn2.set(turn2.x * BODY_HOLD.toolPitch, turn2.y, 0, "YXZ"));
|
|
38998
|
+
holder.position.set(0, 0, 0);
|
|
38999
|
+
hand.updateMatrixWorld(true);
|
|
39000
|
+
if (!placement.right) return;
|
|
39001
|
+
const grip = placement.handgun || placement.left ? BODY_HOLD.handgunGrip : BODY_HOLD.toolGrip;
|
|
39002
|
+
const want = scratch.a.set(grip[0], grip[1], grip[2]).applyQuaternion(hand.quaternion).add(hand.position);
|
|
39003
|
+
const now = placement.right.part.localToWorld(scratch.b.copy(placement.right.offset));
|
|
39004
|
+
hand.position.add(want.sub(now));
|
|
39005
|
+
hand.updateMatrixWorld(true);
|
|
39006
|
+
};
|
|
39007
|
+
var limitSupportSpan = (targets) => {
|
|
39008
|
+
const right = targets.R?.position;
|
|
39009
|
+
const left = targets.L?.position;
|
|
39010
|
+
if (!right || !left || !targets.L) return targets;
|
|
39011
|
+
const span = Math.hypot(left[0] - right[0], left[1] - right[1], left[2] - right[2]);
|
|
39012
|
+
if (span <= BODY_HOLD.supportSpan) return targets;
|
|
39013
|
+
const k = BODY_HOLD.supportSpan / span;
|
|
39014
|
+
const position = [right[0] + (left[0] - right[0]) * k, right[1] + (left[1] - right[1]) * k, right[2] + (left[2] - right[2]) * k];
|
|
39015
|
+
return { ...targets, L: { ...targets.L, position } };
|
|
39016
|
+
};
|
|
39017
|
+
var createEyeFrame = (THREE, camera2) => {
|
|
39018
|
+
const frame2 = { position: [0, 0, 0], quaternion: [0, 0, 0, 1] };
|
|
39019
|
+
const turn2 = new THREE.Euler(0, 0, 0, "YXZ");
|
|
39020
|
+
const pocket = new THREE.Vector3();
|
|
39021
|
+
return () => {
|
|
39022
|
+
turn2.setFromQuaternion(camera2.quaternion, "YXZ");
|
|
39023
|
+
pocket.set(...BODY_HOLD.eyeToPocket).applyEuler(turn2.set(0, turn2.y, 0)).add(camera2.position);
|
|
39024
|
+
frame2.position = [pocket.x, pocket.y, pocket.z];
|
|
39025
|
+
frame2.quaternion = [camera2.quaternion.x, camera2.quaternion.y, camera2.quaternion.z, camera2.quaternion.w];
|
|
39026
|
+
return frame2;
|
|
39027
|
+
};
|
|
39028
|
+
};
|
|
39029
|
+
|
|
38577
39030
|
// src/fx/hand-hold.ts
|
|
38578
39031
|
var HAND_HOLD = {
|
|
38579
39032
|
/** Half the width of a pistol grip, the palm sits this far (plus the palm) off its middle. */
|
|
@@ -39847,6 +40300,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
39847
40300
|
liquid: "honey",
|
|
39848
40301
|
fill: 0.9
|
|
39849
40302
|
}
|
|
40303
|
+
},
|
|
40304
|
+
shapes: {
|
|
40305
|
+
box: {
|
|
40306
|
+
hollow: true,
|
|
40307
|
+
welded: true
|
|
40308
|
+
}
|
|
39850
40309
|
}
|
|
39851
40310
|
};
|
|
39852
40311
|
var DESTRUCTION_MATERIAL_BY_TAG = {
|
|
@@ -39860,6 +40319,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
39860
40319
|
};
|
|
39861
40320
|
var FAMILIES = destruction_default.families;
|
|
39862
40321
|
var CONTENTS = destruction_default.contents;
|
|
40322
|
+
var SHAPES2 = destruction_default.shapes;
|
|
40323
|
+
var shapeDestruction = (shapeId) => SHAPES2[shapeId];
|
|
39863
40324
|
var FAMILY_OF_MODEL = new Map(Object.entries(FAMILIES).flatMap(([family, entry2]) => entry2.models.map((model) => [model, family])));
|
|
39864
40325
|
var itemDestruction = (modelId) => {
|
|
39865
40326
|
const family = FAMILY_OF_MODEL.get(modelId);
|
|
@@ -39973,9 +40434,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
39973
40434
|
root.userData.tessellationTriangles = added;
|
|
39974
40435
|
}
|
|
39975
40436
|
function tagForDestruction(root, options = {}) {
|
|
39976
|
-
const { modelId, weight, size, three } = options;
|
|
40437
|
+
const { modelId, shapeId, weight, size, powder, three } = options;
|
|
39977
40438
|
if (weight !== void 0 && weight > 0) root.userData.destructionMass = weight;
|
|
39978
|
-
const rule = modelId
|
|
40439
|
+
const rule = modelId !== void 0 ? itemDestruction(modelId) : shapeId !== void 0 ? shapeDestruction(shapeId) : void 0;
|
|
39979
40440
|
if (rule?.tessellate) tessellateShapes(root, rule.tessellate, three);
|
|
39980
40441
|
const solid2 = [];
|
|
39981
40442
|
root.traverse((object2) => {
|
|
@@ -39990,6 +40451,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
39990
40451
|
return;
|
|
39991
40452
|
}
|
|
39992
40453
|
if (rule?.hollow) mesh3.userData.hollow = true;
|
|
40454
|
+
if (powder) mesh3.userData.destructionPowder = { color: powder.color };
|
|
39993
40455
|
solid2.push(mesh3);
|
|
39994
40456
|
});
|
|
39995
40457
|
if (!rule?.welded || solid2.length < 2) return;
|
|
@@ -42467,10 +42929,41 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42467
42929
|
const m = metal2(9277590);
|
|
42468
42930
|
return G(mesh3(cyl(0.32, 0.32, 0.52), m), mesh3(cyl(0.326, 0.326, 0.34, 14), food(10172964)), mesh3(cyl(0.33, 0.33, 0.06), paper2(14861162), [0, 0.02, 0]), mesh3(torus2(0.31, 0.025), m, [0, 0.26, 0], [Math.PI / 2, 0, 0]), mesh3(torus2(0.31, 0.025), m, [0, -0.26, 0], [Math.PI / 2, 0, 0]));
|
|
42469
42931
|
};
|
|
42470
|
-
var
|
|
42932
|
+
var opening_default = {
|
|
42933
|
+
families: {
|
|
42934
|
+
drink_can: { style: "pull_tab" },
|
|
42935
|
+
food_tin: { style: "cut_lid" }
|
|
42936
|
+
},
|
|
42937
|
+
models: {
|
|
42938
|
+
empty_can: { style: "none" },
|
|
42939
|
+
canned_beans: { food: "#7a2e1c" },
|
|
42940
|
+
canned_corn_and_peas: { food: "#c7b03c" },
|
|
42941
|
+
canned_sausages: { food: "#c98d5c" },
|
|
42942
|
+
canned_sardines: { style: "peel_lid", food: "#8e979c" },
|
|
42943
|
+
condensed_milk: { food: "#e3cf9c" },
|
|
42944
|
+
energy_drink: { style: "pull_tab" },
|
|
42945
|
+
honey_jar: { style: "screw_cap", food: "#d99a1c" },
|
|
42946
|
+
home_preserves: { style: "screw_cap", food: "#8e1f3a" },
|
|
42947
|
+
cooking_oil: { style: "screw_cap", food: "#e3b830" },
|
|
42948
|
+
water_bottle: { style: "screw_cap" },
|
|
42949
|
+
water_jug_5l: { style: "screw_cap" },
|
|
42950
|
+
dirty_water: { style: "screw_cap" },
|
|
42951
|
+
sports_drink: { style: "screw_cap" },
|
|
42952
|
+
coconut_water: { style: "screw_cap" },
|
|
42953
|
+
cachaca: { style: "screw_cap" },
|
|
42954
|
+
communion_wine: { style: "screw_cap" }
|
|
42955
|
+
}
|
|
42956
|
+
};
|
|
42957
|
+
var FAMILIES2 = opening_default.families;
|
|
42958
|
+
var MODELS = opening_default.models;
|
|
42959
|
+
var asLid = (mesh3) => {
|
|
42960
|
+
mesh3.userData.itemLid = true;
|
|
42961
|
+
return mesh3;
|
|
42962
|
+
};
|
|
42963
|
+
var waterBottle = ({ mesh: mesh3, cyl, lathe: lathe2, G, plastic, paper: paper2, glass }) => G(mesh3(lathe2([[0, -0.6], [0.2, -0.6], [0.22, -0.5], [0.22, 0.2], [0.19, 0.32], [0.09, 0.45], [0.08, 0.55], [0, 0.55]]), glass(9421788, 0.55)), asLid(mesh3(cyl(0.085, 0.085, 0.1, 12), plastic(2907530), [0, 0.6, 0])), mesh3(cyl(0.225, 0.225, 0.22, 16), paper2(15330543), [0, -0.15, 0]), mesh3(cyl(0.226, 0.226, 0.05, 16), plastic(3831976), [0, -0.12, 0]));
|
|
42471
42964
|
var rice = ({ mesh: mesh3, bx, G, paper: paper2, food }) => G(mesh3(bx(0.7, 0.95, 0.28), paper2(15129792)), mesh3(bx(0.72, 0.12, 0.1), paper2(14010792), [0, 0.52, 0]), mesh3(bx(0.5, 0.3, 0.02), food(11883039), [0, 0.05, 0.15]), mesh3(bx(0.3, 0.1, 0.02), paper2(4160069), [0, -0.25, 0.15]));
|
|
42472
42965
|
var militaryRation = ({ mesh: mesh3, bx, G, plastic, paper: paper2 }) => G(mesh3(bx(0.75, 1, 0.22), plastic(5923142)), mesh3(bx(0.77, 0.1, 0.23), plastic(4672823), [0, 0.48, 0]), mesh3(bx(0.5, 0.14, 0.01), paper2(2237727), [0, 0.18, 0.115]), mesh3(bx(0.36, 0.06, 0.01), paper2(2237727), [0, -0.02, 0.115]));
|
|
42473
|
-
var cachaca = ({ mesh: mesh3, cyl, lathe: lathe2, G, wood, paper: paper2, food, glass }) => G(mesh3(lathe2([[0, -0.6], [0.25, -0.6], [0.27, -0.52], [0.27, 0.1], [0.2, 0.25], [0.09, 0.35], [0.09, 0.58], [0, 0.58]]), glass(12749354, 0.78)), mesh3(cyl(0.08, 0.07, 0.14, 10), wood(9071172), [0, 0.64, 0]), mesh3(cyl(0.275, 0.275, 0.34, 16), paper2(15327692), [0, -0.25, 0]), mesh3(cyl(0.276, 0.276, 0.06, 16), food(8007455), [0, -0.15, 0]));
|
|
42966
|
+
var cachaca = ({ mesh: mesh3, cyl, lathe: lathe2, G, wood, paper: paper2, food, glass }) => G(mesh3(lathe2([[0, -0.6], [0.25, -0.6], [0.27, -0.52], [0.27, 0.1], [0.2, 0.25], [0.09, 0.35], [0.09, 0.58], [0, 0.58]]), glass(12749354, 0.78)), asLid(mesh3(cyl(0.08, 0.07, 0.14, 10), wood(9071172), [0, 0.64, 0])), mesh3(cyl(0.275, 0.275, 0.34, 16), paper2(15327692), [0, -0.25, 0]), mesh3(cyl(0.276, 0.276, 0.06, 16), food(8007455), [0, -0.15, 0]));
|
|
42474
42967
|
var firstAidKit = ({ mesh: mesh3, bx, torus: torus2, G, plastic }) => {
|
|
42475
42968
|
const r = plastic(11680300);
|
|
42476
42969
|
return G(mesh3(bx(1.05, 0.72, 0.38), plastic(15131608)), mesh3(bx(1.07, 0.06, 0.4), plastic(12433578), [0, 0, 0]), mesh3(bx(0.36, 0.1, 0.02), r, [0, 0.1, 0.2]), mesh3(bx(0.1, 0.36, 0.02), r, [0, 0.1, 0.2]), mesh3(torus2(0.16, 0.035, Math.PI), plastic(5591884), [0, 0.36, 0]));
|
|
@@ -42630,7 +43123,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42630
43123
|
mesh3(cyl(0.41, 0.41, 0.86, 12), food(9314106), [0, -0.2, 0]),
|
|
42631
43124
|
mesh3(cyl(0.34, 0.445, 0.12, 12), glass(13625070, 0.5), [0, 0.39, 0]),
|
|
42632
43125
|
mesh3(cyl(0.34, 0.34, 0.05, 12), glass(13625070, 0.5), [0, 0.475, 0]),
|
|
42633
|
-
mesh3(cyl(0.38, 0.38, 0.15, 12), metal2(12163644), [0, 0.575, 0]),
|
|
43126
|
+
asLid(mesh3(cyl(0.38, 0.38, 0.15, 12), metal2(12163644), [0, 0.575, 0])),
|
|
42634
43127
|
mesh3(cyl(0.45, 0.45, 0.4, 12), paper2(15327172), [0, -0.2, 0]),
|
|
42635
43128
|
mesh3(cyl(0.452, 0.452, 0.08, 12), cloth2(10172986), [0, -0.2, 0])
|
|
42636
43129
|
);
|
|
@@ -42805,7 +43298,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42805
43298
|
mesh3(cyl(0.395, 0.395, 0.68, 12), glass(14925936, 0.55), [0, -0.16, 0]),
|
|
42806
43299
|
mesh3(cyl(0.37, 0.37, 0.6, 12), food(14260764), [0, -0.2, 0]),
|
|
42807
43300
|
mesh3(cyl(0.3, 0.395, 0.14, 12), glass(14925936, 0.55), [0, 0.25, 0]),
|
|
42808
|
-
mesh3(cyl(0.3, 0.3, 0.18, 12), metal2(7031324), [0, 0.41, 0]),
|
|
43301
|
+
asLid(mesh3(cyl(0.3, 0.3, 0.18, 12), metal2(7031324), [0, 0.41, 0])),
|
|
42809
43302
|
mesh3(cyl(0.4, 0.4, 0.34, 12), paper2(15853512), [0, -0.2, 0]),
|
|
42810
43303
|
mesh3(cyl(0.402, 0.402, 0.05, 12), paper2(9067028), [0, -0.1, 0]),
|
|
42811
43304
|
mesh3(cyl(0.402, 0.402, 0.05, 12), paper2(9067028), [0, -0.3, 0])
|
|
@@ -42891,7 +43384,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42891
43384
|
mesh3(cyl(0.345, 0.345, 1.7, 12), glass(14923824, 0.65), [0, -0.45, 0]),
|
|
42892
43385
|
mesh3(cyl(0.12, 0.345, 0.5, 12), glass(14923824, 0.65), [0, 0.65, 0]),
|
|
42893
43386
|
mesh3(cyl(0.12, 0.12, 0.25, 12), glass(14923824, 0.65), [0, 1.025, 0]),
|
|
42894
|
-
mesh3(cyl(0.14, 0.14, 0.15, 12), plastic(4160042), [0, 1.225, 0]),
|
|
43387
|
+
asLid(mesh3(cyl(0.14, 0.14, 0.15, 12), plastic(4160042), [0, 1.225, 0])),
|
|
42895
43388
|
mesh3(cyl(0.35, 0.35, 0.8, 12), paper2(15853512), [0, -0.5, 0]),
|
|
42896
43389
|
mesh3(cyl(0.352, 0.352, 0.2, 12), paper2(4160042), [0, -0.5, 0])
|
|
42897
43390
|
);
|
|
@@ -42923,7 +43416,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42923
43416
|
const body = mesh3(bx(1.5, 2.3, 1.5), glass(10473710, 0.75), [0, -0.5, 0]);
|
|
42924
43417
|
const shoulder = mesh3(bx(1.2, 0.45, 1.2), glass(10473710, 0.75), [0, 0.875, 0]);
|
|
42925
43418
|
const neck = mesh3(cyl(0.34, 0.34, 0.3, 12), dark, [0, 1.25, 0]);
|
|
42926
|
-
const cap = mesh3(cyl(0.4, 0.4, 0.25, 12), dark, [0, 1.525, 0]);
|
|
43419
|
+
const cap = asLid(mesh3(cyl(0.4, 0.4, 0.25, 12), dark, [0, 1.525, 0]));
|
|
42927
43420
|
const grip = mesh3(bx(0.16, 0.9, 0.3), dark, [1.07, 0.6, 0]);
|
|
42928
43421
|
const upperBar = mesh3(bx(0.45, 0.16, 0.3), dark, [0.925, 0.97, 0]);
|
|
42929
43422
|
const lowerBar = mesh3(bx(0.45, 0.16, 0.3), dark, [0.925, 0.23, 0]);
|
|
@@ -42936,7 +43429,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42936
43429
|
const label = mesh3(cyl(0.332, 0.332, 0.7, 12), paper2(1448738), [0, -0.12, 0]);
|
|
42937
43430
|
const stripe = mesh3(cyl(0.332, 0.332, 0.1, 12), paper2(15753232), [0, 0.28, 0]);
|
|
42938
43431
|
const collar = mesh3(cyl(0.2, 0.2, 0.04, 12), plastic(15263976), [0, 0.705, 0]);
|
|
42939
|
-
const cap = mesh3(cyl(0.19, 0.19, 0.2, 12), plastic(15753232), [0, 0.815, 0]);
|
|
43432
|
+
const cap = asLid(mesh3(cyl(0.19, 0.19, 0.2, 12), plastic(15753232), [0, 0.815, 0]));
|
|
42940
43433
|
return G(bottle2, label, stripe, collar, cap);
|
|
42941
43434
|
};
|
|
42942
43435
|
var milkCarton = ({ mesh: mesh3, bx, cyl, extrude: extrude2, G, paper: paper2, plastic }) => {
|
|
@@ -42975,7 +43468,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42975
43468
|
const goldBand = mesh3(cyl(0.382, 0.382, 0.05, 12), metal2(11896880), [0, 0.075, 0]);
|
|
42976
43469
|
const crossUpright = mesh3(bx(0.06, 0.26, 0.02), paper2(8003371), [0, -0.35, 0.375]);
|
|
42977
43470
|
const crossArm = mesh3(bx(0.18, 0.06, 0.02), paper2(8003371), [0, -0.3, 0.375]);
|
|
42978
|
-
const foil = mesh3(cyl(0.15, 0.15, 0.46, 12), metal2(11896880), [0, 1.27, 0]);
|
|
43471
|
+
const foil = asLid(mesh3(cyl(0.15, 0.15, 0.46, 12), metal2(11896880), [0, 1.27, 0]));
|
|
42979
43472
|
return G(bottle2, label, goldBand, crossUpright, crossArm, foil);
|
|
42980
43473
|
};
|
|
42981
43474
|
var coconutWater = ({ mesh: mesh3, bx, cyl, lathe: lathe2, G, plastic, paper: paper2 }) => {
|
|
@@ -42984,7 +43477,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42984
43477
|
const nut = mesh3(cyl(0.11, 0.11, 0.03, 10), paper2(7031339), [0, -0.12, 0.285], [Math.PI / 2, 0, 0]);
|
|
42985
43478
|
const leafLeft = mesh3(bx(0.2, 0.05, 0.01), paper2(15135448), [-0.12, 0.08, 0.292], [0, 0, 0.5]);
|
|
42986
43479
|
const leafRight = mesh3(bx(0.2, 0.05, 0.01), paper2(15135448), [0.12, 0.08, 0.292], [0, 0, -0.5]);
|
|
42987
|
-
const cap = mesh3(cyl(0.14, 0.14, 0.2, 12), plastic(4165450), [0, 0.75, 0]);
|
|
43480
|
+
const cap = asLid(mesh3(cyl(0.14, 0.14, 0.2, 12), plastic(4165450), [0, 0.75, 0]));
|
|
42988
43481
|
return G(bottle2, label, nut, leafLeft, leafRight, cap);
|
|
42989
43482
|
};
|
|
42990
43483
|
var dirtyWater = ({ mesh: mesh3, cyl, lathe: lathe2, G, plastic, paper: paper2, glass, food }) => {
|
|
@@ -42992,7 +43485,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42992
43485
|
const murk = mesh3(lathe2([[0, -0.89], [0.27, -0.89], [0.315, -0.84], [0.315, 0.1], [0, 0.1]], 12), food(7035444));
|
|
42993
43486
|
const sediment = mesh3(cyl(0.31, 0.27, 0.12, 8), food(4865314), [0, -0.82, 0]);
|
|
42994
43487
|
const label = mesh3(cyl(0.332, 0.332, 0.22, 12), paper2(13617588), [0, 0.38, 0]);
|
|
42995
|
-
const cap = mesh3(cyl(0.19, 0.19, 0.2, 8), plastic(5922920), [0, 0.815, 0]);
|
|
43488
|
+
const cap = asLid(mesh3(cyl(0.19, 0.19, 0.2, 8), plastic(5922920), [0, 0.815, 0]));
|
|
42996
43489
|
return G(bottle2, murk, sediment, label, cap);
|
|
42997
43490
|
};
|
|
42998
43491
|
var painkillers = ({ mesh: mesh3, bx, cyl, G, paper: paper2, metal: metal2, glass }) => {
|
|
@@ -45608,7 +46101,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
45608
46101
|
const materials = kit.materials.slice(first);
|
|
45609
46102
|
if (options.wear) setWear(materials, options.wear);
|
|
45610
46103
|
root.userData.wear = options.wear ?? 0;
|
|
45611
|
-
if (options.destructible) tagForDestruction(root, { ...kind === "model" && item.model !== void 0 ? { modelId: item.model } : {}, ...item.weight === void 0 ? {} : { weight: item.weight }, size: item.size, three: T2 });
|
|
46104
|
+
if (options.destructible) tagForDestruction(root, { ...kind === "model" && item.model !== void 0 ? { modelId: item.model } : {}, ...shape === void 0 ? {} : { shapeId: shape }, ...item.weight === void 0 ? {} : { weight: item.weight }, size: item.size, ...item.powder ? { powder: item.powder } : {}, three: T2 });
|
|
45612
46105
|
markKitOrigin(root, { owner: "game-items-three", kind: "item", id: item.id, ...kind === "shape" ? { standIn: true } : {} });
|
|
45613
46106
|
return { root, kind, ...shape ? { shape } : {}, size: item.size, materials };
|
|
45614
46107
|
}
|
|
@@ -48895,7 +49388,6 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
48895
49388
|
var MAX_CASINGS = 120;
|
|
48896
49389
|
var MAX_MAGAZINES = 12;
|
|
48897
49390
|
var LIFETIME_S = 25;
|
|
48898
|
-
var HAND_OFFSET = [0.2, -0.2, -0.42];
|
|
48899
49391
|
var GRAVITY2 = 9.81;
|
|
48900
49392
|
function createBoxFx(options) {
|
|
48901
49393
|
const { THREE, scene, camera: camera2, arsenal, groundAt } = options;
|
|
@@ -48917,6 +49409,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
48917
49409
|
let dip = 0;
|
|
48918
49410
|
let reloading = false;
|
|
48919
49411
|
const v = new THREE.Vector3();
|
|
49412
|
+
const v2 = new THREE.Vector3();
|
|
49413
|
+
const eyeFrame = createEyeFrame(THREE, camera2);
|
|
49414
|
+
let rear = 0;
|
|
49415
|
+
let lengthMetres = 0;
|
|
48920
49416
|
const q = new THREE.Quaternion();
|
|
48921
49417
|
const q2 = new THREE.Quaternion();
|
|
48922
49418
|
const pointForward = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2);
|
|
@@ -48928,13 +49424,17 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
48928
49424
|
if (!def || BODY_ACTIONS.has(def.id)) return;
|
|
48929
49425
|
model = buildWeaponModel(THREE, def);
|
|
48930
49426
|
const length2 = new THREE.Box3().setFromObject(model).getSize(v).x;
|
|
48931
|
-
|
|
49427
|
+
rear = new THREE.Box3().setFromObject(model).min.x;
|
|
49428
|
+
lengthMetres = length2;
|
|
48932
49429
|
model.quaternion.copy(pointForward);
|
|
48933
49430
|
hand.add(model);
|
|
48934
49431
|
inHand = def.id;
|
|
48935
|
-
hold = holdOfProp(THREE, model, def.category,
|
|
49432
|
+
hold = holdOfProp(THREE, model, def.category, 1);
|
|
49433
|
+
};
|
|
49434
|
+
const handPoint = (x, y, z, out) => {
|
|
49435
|
+
if (!model) return out.set(x, y, z).applyQuaternion(camera2.quaternion).add(camera2.position);
|
|
49436
|
+
return out.set(x, y, z).applyQuaternion(camera2.quaternion).add(model.getWorldPosition(v2));
|
|
48936
49437
|
};
|
|
48937
|
-
const handPoint = (x, y, z, out) => out.set(HAND_OFFSET[0] + x, HAND_OFFSET[1] + y, HAND_OFFSET[2] + z).applyQuaternion(camera2.quaternion).add(camera2.position);
|
|
48938
49438
|
const throwBody = (kind, mesh3, from, velocity, radius) => {
|
|
48939
49439
|
if (!(kind === "casing" ? layers.casings : layers.magazines)) {
|
|
48940
49440
|
mesh3.geometry.dispose();
|
|
@@ -49045,10 +49545,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
49045
49545
|
update(dt) {
|
|
49046
49546
|
kick *= Math.exp(-dt * 14);
|
|
49047
49547
|
dip += ((reloading ? 1 : 0) - dip) * Math.min(1, dt * 8);
|
|
49048
|
-
if (inHand) {
|
|
49049
|
-
|
|
49548
|
+
if (inHand && model) {
|
|
49549
|
+
const handgun = def?.category === "pistol" || def?.category === "revolver";
|
|
49550
|
+
placeOnBody({ THREE, hand, holder: model, frame: options.bodyHold?.() ?? eyeFrame(), rear, lengthMetres, handgun, right: hold?.right ?? null, left: hold?.left ?? null });
|
|
49551
|
+
hand.position.add(v.set(0, -0.12 * dip, 0.05 * kick).applyQuaternion(camera2.quaternion));
|
|
49050
49552
|
q.setFromEuler(new THREE.Euler(0.14 * kick - 0.25 * dip, 0, 0.6 * dip));
|
|
49051
|
-
hand.quaternion.
|
|
49553
|
+
hand.quaternion.multiply(q);
|
|
49052
49554
|
}
|
|
49053
49555
|
for (const body of [...bodies]) {
|
|
49054
49556
|
stepBody(body, dt);
|
|
@@ -57769,6 +58271,11 @@ void main(){
|
|
|
57769
58271
|
var MAX_CONFORM_STEPS = 48;
|
|
57770
58272
|
var MIN_CONFORM_STEPS = 16;
|
|
57771
58273
|
var BLOOD_GROW_RATE = 1.2;
|
|
58274
|
+
var BLOOD_START_GROWTH = 0.05;
|
|
58275
|
+
var DEFAULT_MAX_GROUND_MARKS = 64;
|
|
58276
|
+
var DEFAULT_MAX_ROCKS = 512;
|
|
58277
|
+
var DEFAULT_MAX_BLOOD = 256;
|
|
58278
|
+
var MAX_SPARE_GROUND_GEOMETRIES = 16;
|
|
57772
58279
|
function createMarkLayer(THREE, options) {
|
|
57773
58280
|
const group = new THREE.Group();
|
|
57774
58281
|
const textures = createMarkTextures(THREE, options.createCanvas);
|
|
@@ -57792,110 +58299,168 @@ void main(){
|
|
|
57792
58299
|
const bloodMaterial = new THREE.MeshStandardMaterial({ color: 6031882, roughness: 0.15, metalness: 0.1, polygonOffset: true, polygonOffsetFactor: -3, polygonOffsetUnits: -3 });
|
|
57793
58300
|
const bloodGeometry = new THREE.CircleGeometry(0.5, 9).rotateX(-Math.PI / 2);
|
|
57794
58301
|
owned.push(bloodMaterial, bloodGeometry);
|
|
57795
|
-
const
|
|
57796
|
-
const
|
|
57797
|
-
|
|
57798
|
-
|
|
57799
|
-
|
|
57800
|
-
|
|
57801
|
-
material = new THREE.MeshStandardMaterial({ color: color2, roughness: 0.9, metalness: 0, flatShading: true });
|
|
57802
|
-
rockMaterials.set(color2, material);
|
|
57803
|
-
owned.push(material);
|
|
58302
|
+
const maxGroundMarks = options.maxGroundMarks ?? DEFAULT_MAX_GROUND_MARKS;
|
|
58303
|
+
const instanced = {};
|
|
58304
|
+
function rockMaterial() {
|
|
58305
|
+
if (!instanced.rockMaterial) {
|
|
58306
|
+
instanced.rockMaterial = new THREE.MeshStandardMaterial({ color: 16777215, roughness: 0.9, metalness: 0, flatShading: true });
|
|
58307
|
+
owned.push(instanced.rockMaterial);
|
|
57804
58308
|
}
|
|
57805
|
-
return
|
|
58309
|
+
return instanced.rockMaterial;
|
|
58310
|
+
}
|
|
58311
|
+
function bloodMesh() {
|
|
58312
|
+
return instanced.blood ?? (instanced.blood = attach(createInstanced(bloodGeometry, bloodMaterial, options.maxBlood ?? DEFAULT_MAX_BLOOD)));
|
|
58313
|
+
}
|
|
58314
|
+
function rockMesh() {
|
|
58315
|
+
return instanced.rock ?? (instanced.rock = attach(createInstanced(options.rockGeometry, rockMaterial(), options.maxRocks ?? DEFAULT_MAX_ROCKS), true));
|
|
58316
|
+
}
|
|
58317
|
+
function plateMesh() {
|
|
58318
|
+
return instanced.plate ?? (instanced.plate = attach(createInstanced(options.plateGeometry, rockMaterial(), options.maxRocks ?? DEFAULT_MAX_ROCKS), true));
|
|
58319
|
+
}
|
|
58320
|
+
function attach(mesh3, castShadow = false) {
|
|
58321
|
+
mesh3.castShadow = castShadow;
|
|
58322
|
+
group.add(mesh3);
|
|
58323
|
+
return mesh3;
|
|
58324
|
+
}
|
|
58325
|
+
const groundMarks = [];
|
|
58326
|
+
const spareGeometries = [];
|
|
58327
|
+
const blood = [];
|
|
58328
|
+
const placed = { blood: 0, rock: 0, plate: 0 };
|
|
58329
|
+
const matrix = new THREE.Matrix4();
|
|
58330
|
+
const position = new THREE.Vector3();
|
|
58331
|
+
const quaternion2 = new THREE.Quaternion();
|
|
58332
|
+
const euler = new THREE.Euler();
|
|
58333
|
+
const scale = new THREE.Vector3();
|
|
58334
|
+
const color2 = new THREE.Color();
|
|
58335
|
+
const up = new THREE.Vector3(0, 1, 0);
|
|
58336
|
+
function createInstanced(geometry, material, capacity) {
|
|
58337
|
+
const mesh3 = new THREE.InstancedMesh(geometry, material, capacity);
|
|
58338
|
+
mesh3.count = 0;
|
|
58339
|
+
mesh3.frustumCulled = false;
|
|
58340
|
+
mesh3.receiveShadow = true;
|
|
58341
|
+
return mesh3;
|
|
58342
|
+
}
|
|
58343
|
+
function takeSpare(vertexCount) {
|
|
58344
|
+
const found = spareGeometries.findIndex((geometry) => geometry.getAttribute("position").count === vertexCount);
|
|
58345
|
+
return found < 0 ? void 0 : spareGeometries.splice(found, 1)[0];
|
|
58346
|
+
}
|
|
58347
|
+
function evictOldestGroundMark() {
|
|
58348
|
+
const oldest = groundMarks.shift();
|
|
58349
|
+
group.remove(oldest.mesh);
|
|
58350
|
+
spareGeometries.push(oldest.mesh.geometry);
|
|
58351
|
+
if (spareGeometries.length > MAX_SPARE_GROUND_GEOMETRIES) spareGeometries.shift().dispose();
|
|
57806
58352
|
}
|
|
57807
58353
|
function conform(mark, material) {
|
|
57808
58354
|
const radius = mark.radius;
|
|
57809
58355
|
const steps = Math.max(MIN_CONFORM_STEPS, Math.min(MAX_CONFORM_STEPS, Math.round(radius * 8)));
|
|
57810
|
-
|
|
57811
|
-
const
|
|
58356
|
+
if (groundMarks.length >= maxGroundMarks) evictOldestGroundMark();
|
|
58357
|
+
const geometry = takeSpare((steps + 1) * (steps + 1)) ?? new THREE.PlaneGeometry(2, 2, steps, steps).rotateX(-Math.PI / 2);
|
|
58358
|
+
const grid = geometry.attributes.position;
|
|
58359
|
+
const cell = 2 / steps;
|
|
57812
58360
|
const cos2 = Math.cos(mark.rotation);
|
|
57813
58361
|
const sin2 = Math.sin(mark.rotation);
|
|
57814
|
-
for (let index = 0; index <
|
|
57815
|
-
const localX =
|
|
57816
|
-
const localZ =
|
|
58362
|
+
for (let index = 0; index < grid.count; index++) {
|
|
58363
|
+
const localX = Math.fround(index % (steps + 1) * cell - 1) * radius;
|
|
58364
|
+
const localZ = Math.fround(Math.floor(index / (steps + 1)) * cell - 1) * radius;
|
|
57817
58365
|
const worldX = mark.x + localX * cos2 - localZ * sin2;
|
|
57818
58366
|
const worldZ = mark.z + localX * sin2 + localZ * cos2;
|
|
57819
|
-
|
|
58367
|
+
grid.setXYZ(index, worldX, (options.ground?.heightAt(worldX, worldZ) ?? 0) + mark.lift, worldZ);
|
|
57820
58368
|
}
|
|
58369
|
+
grid.needsUpdate = true;
|
|
57821
58370
|
geometry.computeVertexNormals();
|
|
58371
|
+
geometry.attributes.normal.needsUpdate = true;
|
|
58372
|
+
geometry.boundingSphere = null;
|
|
58373
|
+
geometry.boundingBox = null;
|
|
57822
58374
|
const mesh3 = new THREE.Mesh(geometry, material);
|
|
57823
58375
|
mesh3.receiveShadow = true;
|
|
57824
|
-
|
|
58376
|
+
group.add(mesh3);
|
|
58377
|
+
groundMarks.push({ mesh: mesh3, steps });
|
|
57825
58378
|
}
|
|
57826
58379
|
function addRock(mark) {
|
|
57827
|
-
const mesh3 =
|
|
57828
|
-
|
|
57829
|
-
|
|
57830
|
-
|
|
57831
|
-
mesh3.
|
|
57832
|
-
mesh3.
|
|
57833
|
-
|
|
58380
|
+
const mesh3 = mark.plate ? plateMesh() : rockMesh();
|
|
58381
|
+
const slot = mark.plate ? "plate" : "rock";
|
|
58382
|
+
const index = placed[slot]++ % mesh3.instanceMatrix.count;
|
|
58383
|
+
quaternion2.setFromEuler(euler.set(mark.rotation[0], mark.rotation[1], mark.rotation[2]));
|
|
58384
|
+
mesh3.setMatrixAt(index, matrix.compose(position.set(mark.x, mark.y, mark.z), quaternion2, scale.set(mark.scale[0], mark.scale[1], mark.scale[2])));
|
|
58385
|
+
mesh3.setColorAt(index, color2.set(mark.color));
|
|
58386
|
+
mesh3.count = Math.min(placed[slot], mesh3.instanceMatrix.count);
|
|
58387
|
+
mesh3.instanceMatrix.needsUpdate = true;
|
|
58388
|
+
mesh3.instanceColor.needsUpdate = true;
|
|
58389
|
+
}
|
|
58390
|
+
function writeBlood(index, drawn, growth) {
|
|
58391
|
+
quaternion2.setFromAxisAngle(up, drawn.rotation);
|
|
58392
|
+
const mesh3 = bloodMesh();
|
|
58393
|
+
mesh3.setMatrixAt(index, matrix.compose(position.set(drawn.position[0], drawn.position[1], drawn.position[2]), quaternion2, scale.set(drawn.scaleX * growth, 1, drawn.scaleZ * growth)));
|
|
58394
|
+
mesh3.instanceMatrix.needsUpdate = true;
|
|
57834
58395
|
}
|
|
57835
58396
|
function addBlood(mark) {
|
|
57836
|
-
const
|
|
57837
|
-
|
|
57838
|
-
|
|
57839
|
-
|
|
57840
|
-
|
|
57841
|
-
mesh3.scale.multiplyScalar(0.05);
|
|
57842
|
-
blood.push({ mesh: mesh3, scaleX: mark.scaleX, scaleZ: mark.scaleZ, age: 0 });
|
|
57843
|
-
return mesh3;
|
|
58397
|
+
const index = placed.blood++ % bloodMesh().instanceMatrix.count;
|
|
58398
|
+
const drawn = { position: [mark.x, mark.y, mark.z], rotation: mark.rotation, scaleX: mark.scaleX, scaleZ: mark.scaleZ, age: 0 };
|
|
58399
|
+
blood[index] = drawn;
|
|
58400
|
+
writeBlood(index, drawn, BLOOD_START_GROWTH);
|
|
58401
|
+
bloodMesh().count = Math.min(placed.blood, bloodMesh().instanceMatrix.count);
|
|
57844
58402
|
}
|
|
57845
58403
|
function build(mark) {
|
|
57846
|
-
if (mark.kind === "scorch")
|
|
57847
|
-
if (mark.kind === "dirt")
|
|
57848
|
-
if (mark.kind === "rock")
|
|
57849
|
-
|
|
57850
|
-
}
|
|
57851
|
-
function
|
|
57852
|
-
for (const
|
|
57853
|
-
|
|
57854
|
-
|
|
57855
|
-
|
|
57856
|
-
|
|
58404
|
+
if (mark.kind === "scorch") conform(mark, scorchMaterials[mark.variant]);
|
|
58405
|
+
else if (mark.kind === "dirt") conform(mark, dirtMaterial);
|
|
58406
|
+
else if (mark.kind === "rock") addRock(mark);
|
|
58407
|
+
else addBlood(mark);
|
|
58408
|
+
}
|
|
58409
|
+
function clearMarks() {
|
|
58410
|
+
for (const drawn of groundMarks) group.remove(drawn.mesh);
|
|
58411
|
+
for (const drawn of groundMarks) drawn.mesh.geometry.dispose();
|
|
58412
|
+
groundMarks.length = 0;
|
|
58413
|
+
for (const geometry of spareGeometries) geometry.dispose();
|
|
58414
|
+
spareGeometries.length = 0;
|
|
57857
58415
|
blood.length = 0;
|
|
57858
|
-
|
|
58416
|
+
placed.blood = placed.rock = placed.plate = 0;
|
|
58417
|
+
for (const mesh3 of [instanced.blood, instanced.rock, instanced.plate]) if (mesh3) mesh3.count = 0;
|
|
57859
58418
|
}
|
|
57860
58419
|
return {
|
|
57861
58420
|
group,
|
|
57862
58421
|
handle(event) {
|
|
57863
|
-
if (event.type === "clear")
|
|
57864
|
-
|
|
57865
|
-
|
|
57866
|
-
|
|
57867
|
-
|
|
57868
|
-
|
|
58422
|
+
if (event.type === "clear") clearMarks();
|
|
58423
|
+
else build(event.mark);
|
|
58424
|
+
},
|
|
58425
|
+
prepare() {
|
|
58426
|
+
bloodMesh();
|
|
58427
|
+
rockMesh();
|
|
58428
|
+
plateMesh();
|
|
57869
58429
|
},
|
|
57870
58430
|
update(dt) {
|
|
57871
|
-
for (
|
|
57872
|
-
|
|
58431
|
+
for (let index = 0; index < blood.length; index++) {
|
|
58432
|
+
const drawn = blood[index];
|
|
58433
|
+
if (!drawn || drawn.age > 1) continue;
|
|
57873
58434
|
drawn.age += dt * BLOOD_GROW_RATE;
|
|
57874
|
-
|
|
57875
|
-
drawn.mesh.scale.set(drawn.scaleX * Math.max(0.05, ease3), 1, drawn.scaleZ * Math.max(0.05, ease3));
|
|
58435
|
+
writeBlood(index, drawn, Math.max(BLOOD_START_GROWTH, 1 - Math.pow(1 - Math.min(1, drawn.age), 2.5)));
|
|
57876
58436
|
}
|
|
57877
58437
|
},
|
|
57878
|
-
count: () =>
|
|
58438
|
+
count: () => groundMarks.length + [instanced.blood, instanced.rock, instanced.plate].filter((mesh3) => mesh3 && mesh3.count > 0).length,
|
|
57879
58439
|
dispose() {
|
|
57880
|
-
|
|
58440
|
+
clearMarks();
|
|
58441
|
+
for (const mesh3 of [instanced.blood, instanced.rock, instanced.plate]) mesh3?.dispose();
|
|
57881
58442
|
for (const item of owned) item.dispose();
|
|
57882
58443
|
}
|
|
57883
58444
|
};
|
|
57884
58445
|
}
|
|
57885
58446
|
var SPHERE_DEFAULT_WIDTH = 8;
|
|
57886
58447
|
var SPHERE_DEFAULT_HEIGHT = 6;
|
|
58448
|
+
var MAX_PARKED_MESHES_PER_KIND = 256;
|
|
57887
58449
|
function geometryOf(THREE, particle) {
|
|
57888
58450
|
switch (particle.shape) {
|
|
57889
58451
|
case "cone":
|
|
57890
|
-
return new THREE.ConeGeometry(
|
|
58452
|
+
return new THREE.ConeGeometry(1, 1, particle.segments || void 0, particle.segments2 || 1, false);
|
|
57891
58453
|
case "cone_open":
|
|
57892
|
-
return new THREE.ConeGeometry(
|
|
58454
|
+
return new THREE.ConeGeometry(1, 1, particle.segments || void 0, particle.segments2 || 1, true);
|
|
57893
58455
|
case "sphere":
|
|
57894
|
-
return new THREE.SphereGeometry(
|
|
58456
|
+
return new THREE.SphereGeometry(1, particle.segments || SPHERE_DEFAULT_WIDTH, particle.segments2 || SPHERE_DEFAULT_HEIGHT);
|
|
57895
58457
|
case "icosahedron":
|
|
57896
|
-
return new THREE.IcosahedronGeometry(
|
|
58458
|
+
return new THREE.IcosahedronGeometry(1, particle.segments);
|
|
57897
58459
|
}
|
|
57898
58460
|
}
|
|
58461
|
+
function isCone(particle) {
|
|
58462
|
+
return particle.shape === "cone" || particle.shape === "cone_open";
|
|
58463
|
+
}
|
|
57899
58464
|
function materialOf(THREE, particle) {
|
|
57900
58465
|
if (particle.lit) return new THREE.MeshStandardMaterial({ color: particle.color, roughness: 1, metalness: 0, transparent: true, opacity: particle.opacity, depthWrite: false });
|
|
57901
58466
|
return new THREE.MeshBasicMaterial({ color: particle.color, transparent: true, opacity: particle.opacity, depthWrite: false, ...particle.additive ? { blending: THREE.AdditiveBlending } : {} });
|
|
@@ -57906,51 +58471,82 @@ void main(){
|
|
|
57906
58471
|
(kind) => new THREE.Mesh(geometry, materialOf(THREE, { ...kind, color: 8421504, opacity: 0.5 }))
|
|
57907
58472
|
);
|
|
57908
58473
|
}
|
|
58474
|
+
function geometryKeyOf(particle) {
|
|
58475
|
+
return `${particle.shape}:${particle.segments}:${particle.segments2}`;
|
|
58476
|
+
}
|
|
57909
58477
|
function createMeshLayer(THREE, pool) {
|
|
57910
58478
|
const group = new THREE.Group();
|
|
57911
58479
|
group.name = "workshop-meshes";
|
|
57912
58480
|
const drawn = /* @__PURE__ */ new Map();
|
|
58481
|
+
const geometries = /* @__PURE__ */ new Map();
|
|
58482
|
+
const parked = /* @__PURE__ */ new Map();
|
|
57913
58483
|
const frame2 = new THREE.Quaternion();
|
|
57914
58484
|
const euler = new THREE.Euler();
|
|
57915
58485
|
const turn2 = new THREE.Quaternion();
|
|
57916
58486
|
const offset = new THREE.Vector3();
|
|
57917
|
-
|
|
57918
|
-
|
|
57919
|
-
|
|
57920
|
-
|
|
58487
|
+
let stamp = 0;
|
|
58488
|
+
function geometryFor(particle) {
|
|
58489
|
+
const key2 = geometryKeyOf(particle);
|
|
58490
|
+
let geometry = geometries.get(key2);
|
|
58491
|
+
if (!geometry) {
|
|
58492
|
+
geometry = geometryOf(THREE, particle);
|
|
58493
|
+
geometries.set(key2, geometry);
|
|
58494
|
+
}
|
|
58495
|
+
return geometry;
|
|
58496
|
+
}
|
|
58497
|
+
function acquire(particle) {
|
|
58498
|
+
const parkingKey = `${geometryKeyOf(particle)}|${particle.lit}|${particle.additive}`;
|
|
58499
|
+
const mesh3 = parked.get(parkingKey)?.pop() ?? new THREE.Mesh(geometryFor(particle), materialOf(THREE, particle));
|
|
58500
|
+
mesh3.material.color.set(particle.color);
|
|
58501
|
+
mesh3.frustumCulled = false;
|
|
58502
|
+
mesh3.renderOrder = particle.additive ? 5 : 3;
|
|
58503
|
+
group.add(mesh3);
|
|
58504
|
+
return { mesh: mesh3, parkingKey, stamp };
|
|
58505
|
+
}
|
|
58506
|
+
function park(entry2) {
|
|
58507
|
+
entry2.mesh.removeFromParent();
|
|
58508
|
+
let free = parked.get(entry2.parkingKey);
|
|
58509
|
+
if (!free) {
|
|
58510
|
+
free = [];
|
|
58511
|
+
parked.set(entry2.parkingKey, free);
|
|
58512
|
+
}
|
|
58513
|
+
if (free.length < MAX_PARKED_MESHES_PER_KIND) free.push(entry2.mesh);
|
|
58514
|
+
else entry2.mesh.material.dispose();
|
|
57921
58515
|
}
|
|
57922
58516
|
return {
|
|
57923
58517
|
group,
|
|
57924
58518
|
sync() {
|
|
57925
|
-
|
|
58519
|
+
stamp++;
|
|
57926
58520
|
for (const particle of pool.particles) {
|
|
57927
|
-
|
|
57928
|
-
|
|
57929
|
-
|
|
57930
|
-
|
|
57931
|
-
mesh3.frustumCulled = false;
|
|
57932
|
-
mesh3.renderOrder = particle.additive ? 5 : 3;
|
|
57933
|
-
drawn.set(particle.id, mesh3);
|
|
57934
|
-
group.add(mesh3);
|
|
58521
|
+
let entry2 = drawn.get(particle.id);
|
|
58522
|
+
if (!entry2) {
|
|
58523
|
+
entry2 = acquire(particle);
|
|
58524
|
+
drawn.set(particle.id, entry2);
|
|
57935
58525
|
}
|
|
58526
|
+
entry2.stamp = stamp;
|
|
58527
|
+
const mesh3 = entry2.mesh;
|
|
57936
58528
|
frame2.set(particle.frame[0], particle.frame[1], particle.frame[2], particle.frame[3]);
|
|
57937
58529
|
offset.set(particle.at[0], particle.at[1], particle.at[2]).multiplyScalar(particle.scale).applyQuaternion(frame2);
|
|
57938
58530
|
mesh3.position.set(particle.x + offset.x, particle.y + offset.y, particle.z + offset.z);
|
|
57939
58531
|
turn2.setFromEuler(euler.set(particle.rx, particle.ry, particle.rz));
|
|
57940
58532
|
mesh3.quaternion.copy(frame2).multiply(turn2);
|
|
57941
|
-
mesh3.scale.
|
|
58533
|
+
mesh3.scale.set(particle.radius * particle.scale, (isCone(particle) ? particle.height : particle.radius) * particle.scale, particle.radius * particle.scale);
|
|
57942
58534
|
mesh3.material.opacity = particle.opacity;
|
|
57943
58535
|
}
|
|
57944
|
-
for (const [id,
|
|
57945
|
-
if (
|
|
57946
|
-
|
|
58536
|
+
for (const [id, entry2] of drawn) {
|
|
58537
|
+
if (entry2.stamp === stamp) continue;
|
|
58538
|
+
park(entry2);
|
|
57947
58539
|
drawn.delete(id);
|
|
57948
58540
|
}
|
|
57949
58541
|
return pool.count;
|
|
57950
58542
|
},
|
|
57951
58543
|
dispose() {
|
|
57952
|
-
for (const
|
|
58544
|
+
for (const entry2 of drawn.values()) park(entry2);
|
|
57953
58545
|
drawn.clear();
|
|
58546
|
+
for (const free of parked.values()) for (const mesh3 of free) mesh3.material.dispose();
|
|
58547
|
+
parked.clear();
|
|
58548
|
+
for (const geometry of geometries.values()) geometry.dispose();
|
|
58549
|
+
geometries.clear();
|
|
57954
58550
|
group.removeFromParent();
|
|
57955
58551
|
}
|
|
57956
58552
|
};
|
|
@@ -58518,7 +59114,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58518
59114
|
const shocks = createShockLayer(THREE, pools.shocks);
|
|
58519
59115
|
const sprites = createSpriteLayer(THREE, world.sprites(), options.pointScale ?? 700);
|
|
58520
59116
|
const lights = createLightLayer(THREE, pools.lights, options.lightScale ?? Math.PI);
|
|
58521
|
-
const marks = createMarkLayer(THREE, { ground: pools.ground, createCanvas: options.createCanvas, rockGeometry, plateGeometry });
|
|
59117
|
+
const marks = createMarkLayer(THREE, { ground: pools.ground, createCanvas: options.createCanvas, rockGeometry, plateGeometry, ...options.markLimits });
|
|
58522
59118
|
root.add(puffs.mesh, sparks.mesh, glows.mesh, chunks.rocks, chunks.plates, shocks.group, lights.group, sprites.additive, sprites.normal, marks.group);
|
|
58523
59119
|
const stopMarks = world.onMarks((event) => marks.handle(event));
|
|
58524
59120
|
const overlay = options.overlay ? createScreenOverlay(options.overlay) : void 0;
|
|
@@ -58544,6 +59140,16 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58544
59140
|
const position = options.camera.position;
|
|
58545
59141
|
world.setCamera([position.x, position.y, position.z]);
|
|
58546
59142
|
}
|
|
59143
|
+
function attachWorkshopLayers() {
|
|
59144
|
+
const engine = world.peekWorkshop();
|
|
59145
|
+
if (!engine || engine === workshopEngine) return;
|
|
59146
|
+
workshopLayers?.dispose();
|
|
59147
|
+
if (workshopLayers) root.remove(workshopLayers.group);
|
|
59148
|
+
workshopEngine = engine;
|
|
59149
|
+
workshopLayers = createWorkshopLayers(THREE, engine, options.pointScale ?? 700, 1);
|
|
59150
|
+
root.add(workshopLayers.group);
|
|
59151
|
+
applyLayers();
|
|
59152
|
+
}
|
|
58547
59153
|
function syncAll() {
|
|
58548
59154
|
lastDrawn.puffs = puffs.sync();
|
|
58549
59155
|
lastDrawn.sparks = sparks.sync();
|
|
@@ -58552,15 +59158,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58552
59158
|
lastDrawn.shocks = shocks.sync();
|
|
58553
59159
|
lastDrawn.sprites = sprites.sync();
|
|
58554
59160
|
lights.sync();
|
|
58555
|
-
|
|
58556
|
-
if (engine && engine !== workshopEngine) {
|
|
58557
|
-
workshopLayers?.dispose();
|
|
58558
|
-
if (workshopLayers) root.remove(workshopLayers.group);
|
|
58559
|
-
workshopEngine = engine;
|
|
58560
|
-
workshopLayers = createWorkshopLayers(THREE, engine, options.pointScale ?? 700, 1);
|
|
58561
|
-
root.add(workshopLayers.group);
|
|
58562
|
-
applyLayers();
|
|
58563
|
-
}
|
|
59161
|
+
attachWorkshopLayers();
|
|
58564
59162
|
const workshopCalls = workshopLayers?.sync() ?? 0;
|
|
58565
59163
|
const glow = pools.lights.glow;
|
|
58566
59164
|
uniforms.uGlowPos.value.set(glow.x, glow.y, glow.z);
|
|
@@ -58613,6 +59211,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58613
59211
|
},
|
|
58614
59212
|
warmUpProbes() {
|
|
58615
59213
|
releaseAnchors();
|
|
59214
|
+
marks.prepare();
|
|
59215
|
+
world.workshop();
|
|
59216
|
+
attachWorkshopLayers();
|
|
58616
59217
|
const meshes = createMeshLayerProbes(THREE);
|
|
58617
59218
|
for (const mesh3 of meshes) mesh3.frustumCulled = false;
|
|
58618
59219
|
const group = new THREE.Group();
|
|
@@ -58679,6 +59280,14 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58679
59280
|
|
|
58680
59281
|
// src/fx/heavy.ts
|
|
58681
59282
|
var PI14 = Math.PI;
|
|
59283
|
+
var ROCKET_LIGHT_NAME = "rocket-light";
|
|
59284
|
+
var rocketsInFlight = /* @__PURE__ */ new WeakMap();
|
|
59285
|
+
function createRocketLight(THREE) {
|
|
59286
|
+
const light = new THREE.PointLight(16751168, 0, 2.5);
|
|
59287
|
+
light.name = ROCKET_LIGHT_NAME;
|
|
59288
|
+
light.position.set(0, -50, 0);
|
|
59289
|
+
return light;
|
|
59290
|
+
}
|
|
58682
59291
|
var flameMaterials = (kit) => {
|
|
58683
59292
|
const { THREE } = kit;
|
|
58684
59293
|
return [
|
|
@@ -58708,8 +59317,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58708
59317
|
exhaust.rotation.z = PI14 / 2;
|
|
58709
59318
|
exhaust.position.x = -0.08;
|
|
58710
59319
|
rocket.add(exhaust);
|
|
58711
|
-
const light =
|
|
58712
|
-
|
|
59320
|
+
const light = context.rocketLight;
|
|
59321
|
+
light.intensity = 3;
|
|
59322
|
+
rocketsInFlight.set(light, (rocketsInFlight.get(light) ?? 0) + 1);
|
|
58713
59323
|
context.every(
|
|
58714
59324
|
1.6,
|
|
58715
59325
|
(progress, dt) => {
|
|
@@ -58717,6 +59327,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58717
59327
|
const distance3 = 1.2 * flight + (flight > 0.25 ? 9 * (flight - 0.25) ** 2 : 0);
|
|
58718
59328
|
rocket.position.copy(muzzle2).addScaledVector(forward, distance3);
|
|
58719
59329
|
rocket.rotation.x += 0.25 * dt * 60;
|
|
59330
|
+
light.position.copy(rocket.position);
|
|
58720
59331
|
exhaust.scale.set(1, 0.6 + random() * 0.8, 1);
|
|
58721
59332
|
if (random() < 0.9) context.effects.smoke(rocket.position.clone().addScaledVector(forward, -0.12), new THREE.Vector3((random() - 0.5) * 0.1, 0.05, (random() - 0.5) * 0.1), 0.02 + random() * 0.015, 2.2, 0.5);
|
|
58722
59333
|
},
|
|
@@ -58724,7 +59335,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58724
59335
|
rocket.removeFromParent();
|
|
58725
59336
|
exhaust.geometry.dispose();
|
|
58726
59337
|
exhaust.material.dispose();
|
|
58727
|
-
|
|
59338
|
+
const remaining = (rocketsInFlight.get(light) ?? 1) - 1;
|
|
59339
|
+
rocketsInFlight.set(light, remaining);
|
|
59340
|
+
if (remaining > 0) return;
|
|
59341
|
+
light.intensity = 0;
|
|
59342
|
+
light.position.set(0, -50, 0);
|
|
58728
59343
|
}
|
|
58729
59344
|
);
|
|
58730
59345
|
}
|
|
@@ -59539,11 +60154,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59539
60154
|
|
|
59540
60155
|
// src/fx/viewmodel.ts
|
|
59541
60156
|
var PI16 = Math.PI;
|
|
59542
|
-
var HAND_OFFSET2 = [0.2, -0.2, -0.42];
|
|
59543
|
-
var HANDGUN_OFFSET = [0.1, -0.12, -0.44];
|
|
59544
60157
|
var HEAT_DISPLAY_MAX_FALLBACK = 120;
|
|
59545
|
-
var WALL_PULL = { margin: 0.03, max:
|
|
59546
|
-
var HAND_IN_VIEW = { floor: -0.88, max: 0.
|
|
60158
|
+
var WALL_PULL = { margin: 0.03, max: 1.2, rate: 14, probe: 3 };
|
|
60159
|
+
var HAND_IN_VIEW = { floor: -0.88, max: 0.3, rate: 10 };
|
|
59547
60160
|
var LAUNCH_VOLUME = 0.6;
|
|
59548
60161
|
var fittedOf2 = (parts) => parts.map((part) => ({ id: part.id, mount: part.mount, ...part.integral ? { integral: true } : {}, ...part.on === void 0 ? {} : { on: part.on } }));
|
|
59549
60162
|
var collapsedStockPose = (move2) => {
|
|
@@ -59573,6 +60186,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59573
60186
|
const hand = new THREE.Group();
|
|
59574
60187
|
hand.name = VIEWMODEL_NODE_NAME;
|
|
59575
60188
|
scene.add(hand);
|
|
60189
|
+
const rocketLight = createRocketLight(THREE);
|
|
60190
|
+
scene.add(rocketLight);
|
|
59576
60191
|
const holder = new THREE.Group();
|
|
59577
60192
|
holder.quaternion.setFromAxisAngle(new THREE.Vector3(0, 1, 0), PI16 / 2);
|
|
59578
60193
|
hand.add(holder);
|
|
@@ -59629,7 +60244,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59629
60244
|
positionOf: (part) => worldPositionOf(current, part, new THREE.Vector3()),
|
|
59630
60245
|
orientation: () => current.model.root.getWorldQuaternion(new THREE.Quaternion()),
|
|
59631
60246
|
every: (seconds, step, done) => void tasks.push({ delay: 0, seconds, elapsed: 0, step, done }),
|
|
59632
|
-
groundAt
|
|
60247
|
+
groundAt,
|
|
60248
|
+
rocketLight
|
|
59633
60249
|
});
|
|
59634
60250
|
const equip = (weapon) => {
|
|
59635
60251
|
const def = arsenal.weapons.get(weapon);
|
|
@@ -59642,14 +60258,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59642
60258
|
reach = 0;
|
|
59643
60259
|
pull = 0;
|
|
59644
60260
|
lift = 0;
|
|
59645
|
-
handHeight = 0;
|
|
59646
|
-
handDepth = 0;
|
|
59647
60261
|
rigStock(THREE, model, def);
|
|
59648
60262
|
rigUnderbarrel(THREE, model, def);
|
|
59649
|
-
const
|
|
59650
|
-
const
|
|
59651
|
-
|
|
59652
|
-
holder.position.set(0, 0, Math.min(0.25, 0.25 * length2 * scale));
|
|
60263
|
+
const modelBox = new THREE.Box3().setFromObject(model.root);
|
|
60264
|
+
const length2 = modelBox.getSize(new THREE.Vector3()).x;
|
|
60265
|
+
modelRear = modelBox.min.x;
|
|
59653
60266
|
holder.add(model.root);
|
|
59654
60267
|
const caliber = caliberOf(arsenal, def);
|
|
59655
60268
|
const dims = caliber?.cartridge;
|
|
@@ -59661,7 +60274,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59661
60274
|
def,
|
|
59662
60275
|
caliber,
|
|
59663
60276
|
model,
|
|
59664
|
-
holderScale:
|
|
60277
|
+
holderScale: 1,
|
|
59665
60278
|
lengthMetres: length2,
|
|
59666
60279
|
caseType: caliber?.caseType ?? "rifle",
|
|
59667
60280
|
soundProfile: soundProfileOf(weapon, model.parts.family),
|
|
@@ -59691,7 +60304,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59691
60304
|
applyCondition(kit, model, condition);
|
|
59692
60305
|
lastAmmo = options.tool?.().ammo ?? 0;
|
|
59693
60306
|
hand.updateMatrixWorld(true);
|
|
59694
|
-
hold = holdOfModel(THREE, model,
|
|
60307
|
+
hold = holdOfModel(THREE, model, 1);
|
|
59695
60308
|
hold = { ...hold, works: handWorkOf(THREE, model, weapon, hold.right) };
|
|
59696
60309
|
triggerRest = model.parts.trigger?.rotation.z ?? 0;
|
|
59697
60310
|
lastShot = -Infinity;
|
|
@@ -59711,8 +60324,6 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59711
60324
|
let reach = 0;
|
|
59712
60325
|
let pull = 0;
|
|
59713
60326
|
let lift = 0;
|
|
59714
|
-
let handHeight = 0;
|
|
59715
|
-
let handDepth = 0;
|
|
59716
60327
|
const ahead = new THREE.Vector3();
|
|
59717
60328
|
const up = new THREE.Vector3();
|
|
59718
60329
|
const seen = new THREE.Vector3();
|
|
@@ -59725,19 +60336,31 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59725
60336
|
for (const x of [bounds.min.x, bounds.max.x]) for (const y of [bounds.min.y, bounds.max.y]) for (const z of [bounds.min.z, bounds.max.z]) farthest = Math.max(farthest, corner.set(x, y, z).sub(camera2.position).dot(ahead));
|
|
59726
60337
|
return farthest;
|
|
59727
60338
|
};
|
|
60339
|
+
let modelRear = 0;
|
|
60340
|
+
const eyeFrame = createEyeFrame(THREE, camera2);
|
|
60341
|
+
const liftToKeepInView = () => {
|
|
60342
|
+
if (!hold || !session || !("fov" in camera2)) return 0;
|
|
60343
|
+
const tanHalf = Math.tan((camera2.fov ?? 70) * PI16 / 360);
|
|
60344
|
+
eyeTurn.copy(camera2.quaternion).invert();
|
|
60345
|
+
const rest2 = targetsOf(THREE, hold, session.model.root.getWorldQuaternion(rootQuaternion), 0);
|
|
60346
|
+
let needed = 0;
|
|
60347
|
+
for (const [anchor, pose] of [[hold.right, rest2.R], [hold.left, rest2.L]]) {
|
|
60348
|
+
if (!anchor || !pose || anchor.part !== session.model.root) continue;
|
|
60349
|
+
seen.set(...pose.position).sub(camera2.position).applyQuaternion(eyeTurn);
|
|
60350
|
+
if (-seen.z > 0.05) needed = Math.max(needed, HAND_IN_VIEW.floor * -seen.z * tanHalf - seen.y);
|
|
60351
|
+
}
|
|
60352
|
+
return Math.max(0, Math.min(HAND_IN_VIEW.max, needed));
|
|
60353
|
+
};
|
|
59728
60354
|
const place = (dt = 0) => {
|
|
59729
|
-
|
|
59730
|
-
const
|
|
59731
|
-
|
|
59732
|
-
hand.
|
|
59733
|
-
if (session && handDepth > 0.05 && "fov" in camera2) {
|
|
59734
|
-
const tanHalf = Math.tan((camera2.fov ?? 70) * PI16 / 360);
|
|
59735
|
-
const want2 = Math.max(0, Math.min(HAND_IN_VIEW.max, lift + (HAND_IN_VIEW.floor - handHeight) * tanHalf * handDepth));
|
|
59736
|
-
lift += (want2 - lift) * Math.min(1, dt * HAND_IN_VIEW.rate);
|
|
59737
|
-
if (lift > 1e-4) hand.position.addScaledVector(up.set(0, 1, 0).applyQuaternion(camera2.quaternion), lift);
|
|
59738
|
-
}
|
|
60355
|
+
if (!session) return;
|
|
60356
|
+
const family = session.model.parts.family;
|
|
60357
|
+
session.holderScale = 1;
|
|
60358
|
+
placeOnBody({ THREE, hand, holder, frame: options.bodyHold?.() ?? eyeFrame(), rear: modelRear, lengthMetres: session.lengthMetres, handgun: family === "pistol" || family === "revolver", right: hold?.right ?? null, left: hold?.left ?? null });
|
|
59739
60359
|
hand.updateMatrixWorld(true);
|
|
59740
|
-
|
|
60360
|
+
lift += (liftToKeepInView() - lift) * Math.min(1, dt * HAND_IN_VIEW.rate);
|
|
60361
|
+
if (lift > 1e-4) hand.position.addScaledVector(up.set(0, 1, 0).applyQuaternion(camera2.quaternion), lift);
|
|
60362
|
+
hand.updateMatrixWorld(true);
|
|
60363
|
+
if (!options.contact) return;
|
|
59741
60364
|
ahead.set(0, 0, -1).applyQuaternion(camera2.quaternion);
|
|
59742
60365
|
if (reach === 0) reach = Math.max(0.01, measureReach(session));
|
|
59743
60366
|
const free = options.contact(camera2.position, ahead, WALL_PULL.probe);
|
|
@@ -59851,12 +60474,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59851
60474
|
const targets = targetsOf(THREE, hold, session.model.root.getWorldQuaternion(rootQuaternion), magazineHand, hold.works ? workWeights(hold.works, player?.running ?? []) : void 0);
|
|
59852
60475
|
const trigger = session.model.parts.trigger;
|
|
59853
60476
|
if (trigger) targets.fingers = { index: indexCurl(trigger.rotation.z - triggerRest, clock - lastShot) };
|
|
59854
|
-
|
|
59855
|
-
seen.set(...targets.R.position).sub(camera2.position).applyQuaternion(eyeTurn.copy(camera2.quaternion).invert());
|
|
59856
|
-
handDepth = -seen.z;
|
|
59857
|
-
handHeight = handDepth > 0.05 ? seen.y / handDepth / Math.tan((camera2.fov ?? 70) * PI16 / 360) : 0;
|
|
59858
|
-
}
|
|
59859
|
-
return targets;
|
|
60477
|
+
return limitSupportSpan(targets);
|
|
59860
60478
|
},
|
|
59861
60479
|
get inHand() {
|
|
59862
60480
|
return session?.id ?? null;
|
|
@@ -59875,6 +60493,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59875
60493
|
particles.dispose();
|
|
59876
60494
|
ownVfx?.dispose();
|
|
59877
60495
|
hand.removeFromParent();
|
|
60496
|
+
rocketLight.removeFromParent();
|
|
60497
|
+
rocketLight.dispose();
|
|
59878
60498
|
}
|
|
59879
60499
|
};
|
|
59880
60500
|
}
|
|
@@ -59899,6 +60519,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59899
60519
|
...options.attachmentModels ? { attachmentModels: options.attachmentModels } : {},
|
|
59900
60520
|
...options.raycast ? { raycast: options.raycast } : {},
|
|
59901
60521
|
...options.contact ? { contact: options.contact } : {},
|
|
60522
|
+
...options.bodyHold ? { bodyHold: options.bodyHold } : {},
|
|
59902
60523
|
...options.effects ? { effects: options.effects } : {},
|
|
59903
60524
|
...options.flyRocket === void 0 ? {} : { flyRocket: options.flyRocket }
|
|
59904
60525
|
});
|
|
@@ -60674,7 +61295,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
60674
61295
|
var P13_VALVE_MESH = "valve";
|
|
60675
61296
|
var P13_VALVE_COLOR = 12095802;
|
|
60676
61297
|
var P13_WALL = 25e-4;
|
|
60677
|
-
var
|
|
61298
|
+
var SHAPES3 = {
|
|
60678
61299
|
fuel_barrel: { shape: { kind: "cylinder", radius: 0.3, height: 0.9 }, position: [0, 0.45, 0], color: 3104666 },
|
|
60679
61300
|
propane_p13: {
|
|
60680
61301
|
shape: { kind: "cylinder", radius: 0.176, height: 0.49 },
|
|
@@ -60685,7 +61306,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
60685
61306
|
},
|
|
60686
61307
|
car_fuel_tank: { shape: { kind: "box", size: [0.9, 0.25, 0.5] }, position: [0, 0.4, 0], color: 4935509 }
|
|
60687
61308
|
};
|
|
60688
|
-
var WORLD_EXPLOSIVE_IDS = Object.keys(
|
|
61309
|
+
var WORLD_EXPLOSIVE_IDS = Object.keys(SHAPES3);
|
|
60689
61310
|
var SHELL = { thickness: 1e-3, fill: 0.03 };
|
|
60690
61311
|
var explosivePart = (def, shape, scale) => ({
|
|
60691
61312
|
id: "tank",
|
|
@@ -60731,7 +61352,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
60731
61352
|
return [centre.x, centre.y, centre.z];
|
|
60732
61353
|
};
|
|
60733
61354
|
var worldExplosiveShell = (THREE, defId) => {
|
|
60734
|
-
const shape =
|
|
61355
|
+
const shape = SHAPES3[defId];
|
|
60735
61356
|
const model = shape ? modelOf(THREE, shape, 2) : void 0;
|
|
60736
61357
|
if (!model) return void 0;
|
|
60737
61358
|
model.traverse((node) => {
|
|
@@ -60744,7 +61365,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
60744
61365
|
return model;
|
|
60745
61366
|
};
|
|
60746
61367
|
var worldExplosiveItem = (def, name, options = {}) => {
|
|
60747
|
-
const shape =
|
|
61368
|
+
const shape = SHAPES3[def.id];
|
|
60748
61369
|
if (!shape) return void 0;
|
|
60749
61370
|
const scale = () => options.scale?.() ?? 1;
|
|
60750
61371
|
const spec = () => ({ name, parts: [explosivePart(def, shape, scale())], joints: [], tags: ["explosive"] });
|
|
@@ -61179,6 +61800,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
61179
61800
|
raycast: createLaserRaycast(lent.THREE, lent.scene, lent.camera),
|
|
61180
61801
|
// the weapon against a wall asks as often as the laser, on the clock of the simulation (so a fast-forwarded room measures as often per frame)
|
|
61181
61802
|
contact: createLaserRaycast(lent.THREE, lent.scene, lent.camera, { now: () => simulatedMs }),
|
|
61803
|
+
...lent.holdFrame ? { bodyHold: () => lent.holdFrame?.() ?? null } : {},
|
|
61182
61804
|
...lent.effects ? { effects: lent.effects } : {},
|
|
61183
61805
|
...options.attachmentModels ? { attachmentModels: options.attachmentModels } : {},
|
|
61184
61806
|
...options.condition ? { condition: options.condition } : {},
|
|
@@ -61991,7 +62613,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
61991
62613
|
};
|
|
61992
62614
|
for (const { kind, item } of PUSH_KINDS) PUSH_LAYOUT[kind].forEach(([distance3, degrees]) => lay(kind, item, bearing(centre, distance3, degrees)));
|
|
61993
62615
|
if (mode === "grenade") {
|
|
61994
|
-
lay("zombie", "zombie_walker", bearing(centre, 3.4, -
|
|
62616
|
+
lay("zombie", "zombie_walker", bearing(centre, 3.4, -130));
|
|
61995
62617
|
lay("survivor", "person_police_officer", bearing(centre, 7.5, 78));
|
|
61996
62618
|
lay("camp_table", "camp_table", bearing(centre, 3, 90));
|
|
61997
62619
|
}
|
|
@@ -62034,7 +62656,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
62034
62656
|
const outward = ((now[0] - from[0]) * (from[0] - blast.point[0]) + (now[2] - from[2]) * (from[2] - blast.point[2])) / away;
|
|
62035
62657
|
return { ...body, distance: distance3, outward, moved: flat(now, from) };
|
|
62036
62658
|
});
|
|
62037
|
-
const torn = (row) => PUSH_KINDS.some((entry2) => entry2.kind === row.kind) && partsOf(row.objectId) < (partsBefore.get(row.objectId) ?? 0);
|
|
62659
|
+
const torn = (row) => (PUSH_KINDS.some((entry2) => entry2.kind === row.kind) || HEAVY_ITEMS.includes(row.kind)) && partsOf(row.objectId) < (partsBefore.get(row.objectId) ?? 0);
|
|
62038
62660
|
const reached = rows.filter((row) => row.distance < blast.radius * 0.85 && !torn(row));
|
|
62039
62661
|
const movedAway = reached.filter((row) => row.outward >= MOVED);
|
|
62040
62662
|
const least = reached.length === 0 ? 0 : Math.min(...reached.map((row) => row.outward));
|
|
@@ -62047,9 +62669,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
62047
62669
|
expect(near.outward > far.outward, fillText2(text.push_falloff, { what: things[kind] ?? kind, near: near.outward.toFixed(2), nearDistance: near.distance.toFixed(1), far: far.outward.toFixed(2), farDistance: far.distance.toFixed(1) }), { near: near.outward, far: far.outward });
|
|
62048
62670
|
}
|
|
62049
62671
|
for (const row of reached.filter((entry2) => entry2.kind === "zombie" || entry2.kind === "survivor")) {
|
|
62050
|
-
expect(row.outward >= MOVED, fillText2(text.push_person, { who: things[row.kind] ?? row.kind, distance: row.distance.toFixed(1), moved: row.outward.toFixed(2) }), { row });
|
|
62672
|
+
expect(row.outward >= MOVED, fillText2(text.push_person, { who: things[row.kind] ?? row.kind, distance: row.distance.toFixed(1), moved: row.outward.toFixed(2) }), { row: { kind: row.kind, distance: row.distance, outward: row.outward } });
|
|
62051
62673
|
}
|
|
62052
|
-
const table =
|
|
62674
|
+
const table = rows.find((row) => HEAVY_ITEMS.includes(row.kind) && row.distance < blast.radius * 0.85);
|
|
62053
62675
|
if (table) expect(table.moved >= MOVED, fillText2(text.push_heavy, { what: things[table.itemId] ?? table.itemId, moved: table.moved.toFixed(2) }), { table });
|
|
62054
62676
|
}
|
|
62055
62677
|
};
|